code: def test_graph_trainer_fingerprints_and_caches(
      temp_cache: TrainingCache,
      tmp_path: Path,
      train_with_schema: Callable,
      spy_on_all_components: Callable,
  ):
      input_file = tmp_path / "input_file.txt"
      input_file.write_text("3")
  
      train_schema = GraphSchema(
          {
              "read_file": SchemaNode(
                  needs={},
                  uses=FileReader,
                  fn="read",
                  constructor_name="create",
                  config={"file_path": str(input_file)},
                  is_input=True,
              ),
              "train": SchemaNode(
                  needs={},
                  uses=PersistableTestComponent,
                  fn="train",
                  constructor_name="create",
                  config={"test_value": "4"},
                  is_target=True,
              ),
              "process": SchemaNode(
                  needs={"resource": "train"},
                  uses=PersistableTestComponent,
                  fn="run_inference",
                  constructor_name="load",
                  config={"wrap_output_in_cacheable": True},
              ),
              "add": SchemaNode(
                  needs={"i1": "read_file", "i2": "process"},
                  uses=AddInputs,
                  fn="add",
                  constructor_name="create",
                  config={},
              ),
              "assert_node": SchemaNode(
                  needs={"i": "add"},
                  uses=AssertComponent,
                  fn="run_assert",
                  constructor_name="create",
                  config={"value_to_assert": 7},
                  is_target=True,
              ),
          }
      )
  
      # The first train should call all the components and cache their outputs.
      mocks = spy_on_all_components(train_schema)
      train_with_schema(train_schema, temp_cache)
      assert node_call_counts(mocks) == {
          "read_file": 1,
          "train": 1,
          "process": 1,
          "add": 1,
          "assert_node": 1,
      }
  
      # Nothing has changed so this time so no components will run
      # (just input nodes during fingerprint run).
      mocks = spy_on_all_components(train_schema)
      train_with_schema(train_schema, temp_cache)
      assert node_call_counts(mocks) == {
          "read_file": 1,  # Inputs nodes are always called during the fingerprint run.
          "train": 0,
          "process": 0,
          "add": 0,
          "assert_node": 0,
      }
  
      # As we changed the config of "add", all its descendants will run.
      train_schema.nodes["add"].config["something"] = "new"
      mocks = spy_on_all_components(train_schema)
      train_with_schema(train_schema, temp_cache)
      assert node_call_counts(mocks) == {
          "read_file": 1,  # Inputs nodes are always called during the fingerprint run.
          "train": 0,
          "process": 0,
          "add": 1,
          "assert_node": 1,
      }
  
      # We always run everything when the `force_retraining` flag is set to `True`
      train_schema.nodes["add"].config["something"] = "new"
      mocks = spy_on_all_components(train_schema)
      train_with_schema(train_schema, temp_cache, force_retraining=True)
      assert node_call_counts(mocks) == {
          "read_file": 1,
          "train": 1,
          "process": 1,
          "add": 1,
          "assert_node": 1,
      }
  
imports: import logging
  from pathlib import Path
  from typing import Callable, Dict, Optional, Text, Type, Any
  from unittest.mock import Mock
  from _pytest.logging import LogCaptureFixture
  from _pytest.monkeypatch import MonkeyPatch
  from _pytest.tmpdir import TempPathFactory
  import pytest
  from rasa.engine.caching import LocalTrainingCache, TrainingCache
  from rasa.engine.exceptions import GraphComponentException
  from rasa.engine.graph import GraphComponent, GraphSchema, SchemaNode, GraphModelConfiguration, GraphNode, ExecutionContext, GraphNodeHook
  from rasa.engine.runner.dask import DaskGraphRunner
  from rasa.engine.storage.local_model_storage import LocalModelStorage
  from rasa.engine.storage.resource import Resource
  from rasa.engine.storage.storage import ModelStorage
  from rasa.engine.training.graph_trainer import GraphTrainer
  from rasa.shared.core.domain import Domain
  from rasa.shared.data import TrainingType
  from rasa.shared.importers.importer import TrainingDataImporter
  from tests.engine.graph_components_test_classes import AddInputs, AssertComponent, FileReader, PersistableTestComponent, ProvideX, SubtractByX, CacheableComponent
label_desc: Use image node_id 2 for calling a global function with example usage: test_graph_trainer_fingerprints_and_caches(temp_cache, tmp_path, train_with_schema, spy_on_all_components) without return types