code: def test_ensemble_no_valid(self):
      print("-" * 80)
      logger.info("test_ensemble_no_valid\n" + "-" * 80 + "\n")
      model = self.run_ensemble(
          valid_frac=0.0,
          expected_precision=9 / 57,
          expected_recall=9 / 9,
      )
      self.assertSequenceEqual(
          model.combiner.models_used, [False, True, False]
      )
  
      # Do a quick test of saving/loading
      model.save(
          "tmp/eval/anom_ensemble_no_valid", save_only_used_models=True
      )
      DetectorEnsemble.load("tmp/eval/anom_ensemble_no_valid")
  
file_path: merlion/tests/evaluate/test_eval_anomaly.py
imports: import logging
  from os.path import abspath, dirname, join
  import sys
  import unittest
  import numpy
  import pandas
  from merlion.evaluate.anomaly import TSADEvaluator, TSADEvaluatorConfig, TSADMetric
  from merlion.models.anomaly.isolation_forest import IsolationForest, IsolationForestConfig
  from merlion.models.anomaly.windstats import WindStats, WindStatsConfig
  from merlion.models.ensemble.anomaly import DetectorEnsemble, DetectorEnsembleConfig, DetectorEnsembleTrainConfig
  from merlion.models.ensemble.combine import ModelSelector, Mean
  from merlion.transform.resample import TemporalResample
  from merlion.utils import TimeSeries
label_desc: Use image node_id 3 for calling the TestEvaluateAnomaly obj's underlying member method code with example usage: obj.test_ensemble_no_valid() without return types