RFC Should cross-validation splitters validate that all classes are represented in each split?

This is a follow-up to the issue raised in https://github.com/scikit-learn/scikit-learn/issues/29554. However, I recall other issues raised for CV estimator in general.

So the context is the following: a CV estimator will use an internal cross-validation scheme. When we deal with a classifier, we don't have any safety mechanism in the CV to make sure that the classifier was at least trained on all classes. This could happen for two reasons on the top of the head: (i) the target is sorted and the training folds does not contain all classes and (ii) a class is potentially underrepresented and not selected.

In all cases, if the `fit` does not fail, we still obtain a broken estimator. If it breaks at `predict` at least this is not silently giving some wrong predictions but this is not a given. However, we don't provide a direct feedback to the user of what went wrong.

So I'm wondering if we should have a sort of mechanism in the CV strategies to ensure that at least all classes have been observed at `fit` time. I don't think that we should touch the estimators because we will repeat a lot of code and fundamentally the issue is raised because of the CV strategies.

NB: the same issue could happen with a simple classifier in a cross-validation to evaluate it. This is not necessarily a CV estimator.

COMMENT:
This particular example is just for trees, where the randomness of the bootstrap helps alleviate this "usually". I imagine, this would occur in other areas of the library where bootstrap is not done. I am not suggesting this is a major bug per se, but when imbalance occurs, there is a chance this degeneracy occurs.

Essentially, the trees that only contain one class is useless and discarded more or less. In other estimators, if the cross-validation results in not all classes being present, it sounds like there might be a silent error(?) Would have to investigate. 

## MWE 
```
@pytest.mark.parametrize("Forest", FOREST_CLASSIFIERS.values())
def test_forest_sampling_imbalance(Forest):
    # Check that the forest can handle imbalanced classes with sampling
    # strategies.
    X, _ = datasets.make_classification(
        n_samples=100, n_features=20, n_informative=4, n_redundant=2, n_classes=2
    )
    y = np.zeros(100)
    y[10:15] = 1

    forest = Forest(
        n_estimators=100,
        random_state=0,
    )
    forest.fit(X, y)
```

Add the following code within `_parallel_build_trees` of `ensemble/_forest.py`

```
from numpy.testing import assert_array_equal
        assert_array_equal(np.unique(y[curr_sample_weight > 0]), np.unique(y)), f"{np.unique(y[curr_sample_weight > 0])} != {np.unique(y)}"
```

## Error trace:
```
>       forest.fit(X, y)

sklearn/ensemble/tests/test_forest.py:1881: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
sklearn/base.py:1241: in wrapper
    return fit_method(estimator, *args, **kwargs)
sklearn/ensemble/_forest.py:489: in fit
    trees = Parallel(
sklearn/utils/parallel.py:77: in __call__
    return super().__call__(iterable_with_config)
../../miniforge3/envs/sklearn-dev/lib/python3.10/site-packages/joblib/parallel.py:1918: in __call__
    return output if self.return_generator else list(output)
../../miniforge3/envs/sklearn-dev/lib/python3.10/site-packages/joblib/parallel.py:1847: in _get_sequential_output
    res = func(*args, **kwargs)
sklearn/utils/parallel.py:139: in __call__
    return self.function(*args, **kwargs)
sklearn/ensemble/_forest.py:183: in _parallel_build_trees
    assert_array_equal(np.unique(y[curr_sample_weight > 0]), np.unique(y)), f"{np.unique(y[curr_sample_weight > 0])} != {np.unique(y)}"
../../miniforge3/envs/sklearn-dev/lib/python3.10/site-packages/numpy/_utils/__init__.py:85: in wrapper
    return fun(*args, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

args = (<built-in function eq>, array([0.]), array([0., 1.]))
kwds = {'err_msg': '', 'header': 'Arrays are not equal', 'strict': False, 'verbose': True}

    @wraps(func)
    def inner(*args, **kwds):
        with self._recreate_cm():
>           return func(*args, **kwds)
E           AssertionError: 
E           Arrays are not equal
E           
E           (shapes (1,), (2,) mismatch)
E            ACTUAL: array([0.])
E            DESIRED: array([0., 1.])

../../miniforge3/envs/sklearn-dev/lib/python3.10/contextlib.py:79: AssertionError
```

Labels: RFC