But, how does one do collision detection on shapes made of small
triangles?
By not doing it.
Collision detection against an arbitrary triangle collection (or worse, between two collections of arbitrary triangles) is prohibitively expensive.
Instead, we typically perform collection detection in a hierarchical fashion, beginning first with extremely crude, simple shapes (like boxes or spheres) that approximate (poorly) the underlying object.
This allows us to quickly reject the majority of potential collisions as "not occurring," which is the usual case. In the case where a such a crude check passes, that indicates a potential actual collision, and we proceed to perform the check against a more-detailed approximation of the underlying shape (such as one made of multiple tightly-fitted capsule shapes, or many aligned bounding boxes).
Essentially, collision detection starts with crude tests and, as those tests pass, continues to more and more detailed representation. This image, provided by Shiro in a comment to Alexandre's answer, illustrates how a complex model (a human) may be represented by a series of simpler shapes:

Rarely do we actually need to perform collision at the individual triangle level, and when we do we've usually used the cruder methods to drill down to a very small set of potential triangles to test against. At that point, specific algorithms are used to perform the tests, just like you'd test if two rectangles overlap. For example, you can determine if and where a ray hits a triangle.