Repository: AllenNeuralDynamics/aind-data-schema

Issue:
add `intended_measurement` to `Channel`
https://github.com/AllenNeuralDynamics/aind-data-schema/blob/95e3882a0b185bcb2e97a98b04f0eb4af0eaf8e0/src/aind_data_schema/components/tile.py#L36-37

This field represents what the (usually fluorescent) signal in this channel is meant to represent. Uncontrolled string.

Discussion:

Solution patch:
diff --git a/examples/exaspim_acquisition.json b/examples/exaspim_acquisition.json
index 44b35bbfd..ae49ecaeb 100644
--- a/examples/exaspim_acquisition.json
+++ b/examples/exaspim_acquisition.json
@@ -85,6 +85,7 @@
          "file_name": "tile_X_0000_Y_0000_Z_0000_CH_488.ims",
          "channel": {
             "channel_name": "488",
+            "intended_measurement": null,
             "light_source_name": "Ex_488",
             "filter_names": [
                "Em_600"
@@ -96,6 +97,8 @@
             "excitation_power": 200.0,
             "excitation_power_unit": "milliwatt",
             "filter_wheel_index": 0,
+            "emission_wavelength": null,
+            "emission_wavelength_unit": "nanometer",
             "dilation": null,
             "dilation_unit": "pixel",
             "description": null
@@ -128,6 +131,7 @@
          "file_name": "tile_X_0000_Y_0000_Z_0000_CH_561.ims",
          "channel": {
             "channel_name": "561",
+            "intended_measurement": null,
             "light_source_name": "Ex_561",
             "filter_names": [
                "Em_600"
@@ -139,6 +143,8 @@
             "excitation_power": 200.0,
             "excitation_power_unit": "milliwatt",
             "filter_wheel_index": 0,
+            "emission_wavelength": null,
+            "emission_wavelength_unit": "nanometer",
             "dilation": null,
             "dilation_unit": "pixel",
             "description": null
diff --git a/examples/ophys_session.json b/examples/ophys_session.json
index 08f9f8d83..afa7ba59e 100644
--- a/examples/ophys_session.json
+++ b/examples/ophys_session.json
@@ -57,13 +57,53 @@
                "patch_cord_name": "Patch Cord A",
                "patch_cord_output_power": "40",
                "output_power_unit": "microwatt",
-               "fiber_name": "Fiber A"
+               "fiber_name": "Fiber A",
+               "channel": {
+                  "channel_name": "Channel A",
+                  "intended_measurement": "Dopamine",
+                  "light_source_name": "Laser A",
+                  "filter_names": [
+                     "Excitation filter 410nm"
+                  ],
+                  "detector_name": "Green CMOS",
+                  "additional_device_names": [],
+                  "excitation_wavelength": 410,
+                  "excitation_wavelength_unit": "nanometer",
+                  "excitation_power": 10.0,
+                  "excitation_power_unit": "milliwatt",
+                  "filter_wheel_index": null,
+                  "emission_wavelength": 600,
+                  "emission_wavelength_unit": "nanometer",
+                  "dilation": null,
+                  "dilation_unit": "pixel",
+                  "description": null
+               }
             },
             {
                "patch_cord_name": "Patch Cord B",
                "patch_cord_output_power": "43",
                "output_power_unit": "microwatt",
-               "fiber_name": "Fiber B"
+               "fiber_name": "Fiber B",
+               "channel": {
+                  "channel_name": "Channel B",
+                  "intended_measurement": "Dopamine",
+                  "light_source_name": "Laser B",
+                  "filter_names": [
+                     "Excitation filter 560nm"
+                  ],
+                  "detector_name": "Red CMOS",
+                  "additional_device_names": [],
+                  "excitation_wavelength": 560,
+                  "excitation_wavelength_unit": "nanometer",
+                  "excitation_power": 7.0,
+                  "excitation_power_unit": "milliwatt",
+                  "filter_wheel_index": null,
+                  "emission_wavelength": 700,
+                  "emission_wavelength_unit": "nanometer",
+                  "dilation": null,
+                  "dilation_unit": "pixel",
+                  "description": null
+               }
             }
          ],
          "fiber_modules": [],
@@ -78,7 +118,7 @@
             }
          ],
          "software": [],
-         "notes": "Internal trigger. GRAB-DA2m shows signal. Unclear about GRAB-rAC"
+         "notes": "Internal trigger."
       }
    ],
    "stimulus_epochs": [],
diff --git a/examples/ophys_session.py b/examples/ophys_session.py
index dcf948f2d..1e423e7ce 100644
--- a/examples/ophys_session.py
+++ b/examples/ophys_session.py
@@ -4,6 +4,7 @@
 
 from aind_data_schema_models.modalities import Modality
 
+from aind_data_schema.components.tile import Channel
 from aind_data_schema.core.session import DetectorConfig, FiberConnectionConfig, LaserConfig, Session, Stream
 
 t = datetime(2022, 7, 12, 7, 00, 00, tzinfo=timezone.utc)
@@ -46,15 +47,35 @@
                     patch_cord_output_power=40,
                     output_power_unit="microwatt",
                     fiber_name="Fiber A",
+                    channel=Channel(
+                        channel_name="Channel A",
+                        intended_measurement="Dopamine",
+                        light_source_name="Laser A",
+                        filter_names=["Excitation filter 410nm"],
+                        detector_name="Green CMOS",
+                        excitation_wavelength=410,
+                        excitation_power=10,
+                        emission_wavelength=600,
+                    )
                 ),
                 FiberConnectionConfig(
                     patch_cord_name="Patch Cord B",
                     patch_cord_output_power=43,
                     output_power_unit="microwatt",
                     fiber_name="Fiber B",
+                    channel=Channel(
+                        channel_name="Channel B",
+                        intended_measurement="Dopamine",
+                        light_source_name="Laser B",
+                        filter_names=["Excitation filter 560nm"],
+                        detector_name="Red CMOS",
+                        excitation_wavelength=560,
+                        excitation_power=7,
+                        emission_wavelength=700,
+                    )
                 ),
             ],
-            notes="Internal trigger. GRAB-DA2m shows signal. Unclear about GRAB-rAC",
+            notes="Internal trigger.",
         )
     ],
 )
diff --git a/pyproject.toml b/pyproject.toml
index 172b5eaf0..7b7a8d509 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -15,17 +15,15 @@ dynamic = ["version"]
 
 dependencies = [
     'aind-data-schema-models>=0.5.4, <1.0.0',
-    'dictdiffer',
     'pydantic>=2.7',
-    'inflection',
-    'jsonschema',
     'semver'
 ]
 
 [project.optional-dependencies]
 dev = [
     'aind_data_schema[linters]',
-    'pydantic>=2.7, !=2.9.0, !=2.9.1'
+    'pydantic>=2.7, !=2.9.0, !=2.9.1',
+    'dictdiffer',
 ]
 
 linters = [
diff --git a/src/aind_data_schema/base.py b/src/aind_data_schema/base.py
index 4db7bb65d..de0e66cb4 100644
--- a/src/aind_data_schema/base.py
+++ b/src/aind_data_schema/base.py
@@ -3,6 +3,7 @@
 import json
 import re
 import logging
+import warnings
 from pathlib import Path
 from typing import Any, Generic, Optional, TypeVar, get_args
 
@@ -85,10 +86,13 @@ class AindGeneric(BaseModel, extra="allow"):
 
     @model_validator(mode="after")
     def validate_fieldnames(self):
-        """Ensure that field names do not contain forbidden characters"""
+        """Warn users when field names contain forbidden characters
+
+        These characters will cause issues with MongoDB queries
+        """
         model_dict = json.loads(self.model_dump_json(by_alias=True))
         if is_dict_corrupt(model_dict):
-            raise ValueError("Field names cannot contain '.' or '$'")
+            warnings.warn("MongoDB queries may not work as expected for fields that contain '.' or '$'")
         return self
 
 
diff --git a/src/aind_data_schema/components/devices.py b/src/aind_data_schema/components/devices.py
index 458dbc9a2..5fe8c6175 100644
--- a/src/aind_data_schema/components/devices.py
+++ b/src/aind_data_schema/components/devices.py
@@ -777,6 +777,8 @@ class Treadmill(MousePlatform):
     device_type: Literal["Treadmill"] = "Treadmill"
     treadmill_width: Decimal = Field(..., title="Width of treadmill (mm)")
     width_unit: SizeUnit = Field(default=SizeUnit.CM, title="Width unit")
+    encoder: Device = Field(..., title="Encoder")
+    pulse_per_revolution: int = Field(..., title="Pulse per revolution")
 
 
 class Arena(MousePlatform):
diff --git a/src/aind_data_schema/components/tile.py b/src/aind_data_schema/components/tile.py
index 3ffe8a444..4125cb58d 100644
--- a/src/aind_data_schema/components/tile.py
+++ b/src/aind_data_schema/components/tile.py
@@ -19,17 +19,21 @@ class Channel(AindModel):
     """Description of a channel"""
 
     channel_name: str = Field(..., title="Channel")
+    intended_measurement: Optional[str] = Field(default=None, title="Intended measurement",
+                                                description="What signal is this channel measuring")
     light_source_name: str = Field(..., title="Light source name", description="Must match device name")
     filter_names: List[str] = Field(..., title="Filter names", description="Must match device names")
     detector_name: str = Field(..., title="Detector name", description="Must match device name")
     additional_device_names: List[str] = Field(default=[], title="Additional device names")
     # excitation
     excitation_wavelength: int = Field(..., title="Wavelength", ge=300, le=1000)
-    excitation_wavelength_unit: SizeUnit = Field(default=SizeUnit.NM, title="Laser wavelength unit")
-    excitation_power: float = Field(..., title="Laser power", le=2000)
-    excitation_power_unit: PowerUnit = Field(default=PowerUnit.MW, title="Laser power unit")
+    excitation_wavelength_unit: SizeUnit = Field(default=SizeUnit.NM, title="Wavelength unit")
+    excitation_power: float = Field(..., title="Light source power", le=2000)
+    excitation_power_unit: PowerUnit = Field(default=PowerUnit.MW, title="Light source power unit")
     # emission
-    filter_wheel_index: int = Field(..., title="Filter wheel index")
+    filter_wheel_index: Optional[int] = Field(default=None, title="Filter wheel index")
+    emission_wavelength: Optional[int] = Field(default=None, title="Emission wavelength")
+    emission_wavelength_unit: SizeUnit = Field(default=SizeUnit.NM, title="Wavelength unit")
     # dilation
     dilation: Optional[int] = Field(default=None, title="Dilation (pixels)")
     dilation_unit: SizeUnit = Field(default=SizeUnit.PX, title="Dilation unit")
diff --git a/src/aind_data_schema/core/metadata.py b/src/aind_data_schema/core/metadata.py
index a44a02dfa..cef31194d 100644
--- a/src/aind_data_schema/core/metadata.py
+++ b/src/aind_data_schema/core/metadata.py
@@ -21,7 +21,7 @@
     model_validator,
 )
 
-from aind_data_schema.base import AindCoreModel, is_dict_corrupt, AwareDatetimeWithDefault
+from aind_data_schema.base import AindCoreModel, AwareDatetimeWithDefault
 from aind_data_schema.core.acquisition import Acquisition
 from aind_data_schema.core.data_description import DataDescription
 from aind_data_schema.core.instrument import Instrument
@@ -144,13 +144,11 @@ def validate_core_fields(cls, value, info: ValidationInfo):
         field_name = info.field_name
         field_class = [f for f in get_args(cls.model_fields[field_name].annotation) if inspect.isclass(f)][0]
 
-        # If the input is a json object, we will try to create the field
         if isinstance(value, dict):
             try:
-                core_model = field_class.model_validate_json(value)
-            # If a validation error is raised,
-            # we will construct the field without validation.
-            except ValidationError:
+                core_model = field_class.model_validate(value)
+            except ValidationError as e:
+                logging.warning(f"Error in validating {field_name}: {e}")
                 core_model = field_class.model_construct(**value)
         else:
             core_model = value
@@ -203,7 +201,8 @@ def validate_metadata(self):
                 model_contents = model.model_dump()
                 try:
                     model_class(**model_contents)
-                except ValidationError:
+                except ValidationError as e:
+                    logging.warning(f"Error in {field_name}: {e}")
                     metadata_status = MetadataStatus.INVALID
         # For certain required fields, like subject, if they are not present,
         # mark the metadata record as missing
@@ -319,10 +318,7 @@ def create_metadata_json(
     core_fields = dict()
     for key, value in core_jsons.items():
         if key in CORE_FILES and value is not None:
-            if is_dict_corrupt(value):
-                logging.warning(f"Provided {key} is corrupt! It will be ignored.")
-            else:
-                core_fields[key] = value
+            core_fields[key] = value
     # Create Metadata object and convert to JSON
     # If there are any validation errors, still create it
     # but set MetadataStatus as Invalid
diff --git a/src/aind_data_schema/core/procedures.py b/src/aind_data_schema/core/procedures.py
index 42fec7119..b35cc0142 100644
--- a/src/aind_data_schema/core/procedures.py
+++ b/src/aind_data_schema/core/procedures.py
@@ -714,12 +714,7 @@ class Procedures(AindCoreModel):
     )
     subject_procedures: List[
         Annotated[
-            Union[
-                Surgery,
-                TrainingProtocol,
-                WaterRestriction,
-                OtherSubjectProcedure
-            ],
+            Union[Surgery, TrainingProtocol, WaterRestriction, OtherSubjectProcedure],
             Field(discriminator="procedure_type"),
         ]
     ] = Field(default=[], title="Subject Procedures")
diff --git a/src/aind_data_schema/core/session.py b/src/aind_data_schema/core/session.py
index 5ffcf7e35..a7dd6663e 100644
--- a/src/aind_data_schema/core/session.py
+++ b/src/aind_data_schema/core/session.py
@@ -64,6 +64,7 @@ class FiberConnectionConfig(AindModel):
     patch_cord_output_power: Decimal = Field(..., title="Output power (uW)")
     output_power_unit: PowerUnit = Field(default=PowerUnit.UW, title="Output power unit")
     fiber_name: str = Field(..., title="Fiber name (must match procedure)")
+    channel: Channel = Field(..., title="Channel")
 
 
 class TriggerType(str, Enum):
diff --git a/src/aind_data_schema/utils/schema_version_bump.py b/src/aind_data_schema/utils/schema_version_bump.py
index 36e78e7f2..201ff13e9 100644
--- a/src/aind_data_schema/utils/schema_version_bump.py
+++ b/src/aind_data_schema/utils/schema_version_bump.py
@@ -34,6 +34,30 @@ def __init__(self, commit_message: str = "", json_schemas_location: Path = Path(
         self.commit_message = commit_message
         self.json_schemas_location = json_schemas_location
 
+    def _get_schema_json(self, model: AindCoreModel) -> dict:
+        """
+        Get the json schema of a model
+        Parameters
+        ----------
+        model : AindCoreModel
+          The model to get the json schema of
+
+        Returns
+        -------
+        dict
+          The json schema of the model
+        """
+        default_filename = model.default_filename()
+        if default_filename.find(".") != -1:
+            schema_filename = default_filename[: default_filename.find(".")] + "_schema.json"
+        main_branch_schema_path = self.json_schemas_location / schema_filename
+        if main_branch_schema_path.exists():
+            with open(main_branch_schema_path, "r") as f:
+                main_branch_schema_contents = json.load(f)
+        else:
+            raise FileNotFoundError(f"Schema file not found: {main_branch_schema_path}")
+        return main_branch_schema_contents
+
     def _get_list_of_models_that_changed(self) -> List[AindCoreModel]:
         """
         Get a list of core models that have been updated by comparing the json
@@ -46,20 +70,18 @@ def _get_list_of_models_that_changed(self) -> List[AindCoreModel]:
         schemas_that_need_updating = []
         for core_model in SchemaWriter.get_schemas():
             core_model_json = core_model.model_json_schema()
-            default_filename = core_model.default_filename()
-            if default_filename.find(".") != -1:
-                schema_filename = default_filename[: default_filename.find(".")] + "_schema.json"
-            main_branch_schema_path = self.json_schemas_location / schema_filename
-            if main_branch_schema_path.exists():
-                with open(main_branch_schema_path, "r") as f:
-                    main_branch_schema_contents = json.load(f)
-                diff = dictdiffer.diff(main_branch_schema_contents, core_model_json)
-                if len(list(diff)) > 0:
-                    schemas_that_need_updating.append(core_model)
+            original_schema = self._get_schema_json(core_model)
+
+            diff_list = list(dictdiffer.diff(original_schema, core_model_json))
+
+            print(f"Diff for {core_model.__name__}: {diff_list}")
+            if len(diff_list) > 0:
+                schemas_that_need_updating.append(core_model)
+
+        print(f"Schemas that need updating: {[model.__name__ for model in schemas_that_need_updating]}")
         return schemas_that_need_updating
 
-    @staticmethod
-    def _get_incremented_versions_map(models_that_changed: List[AindCoreModel]) -> Dict[AindCoreModel, str]:
+    def _get_incremented_versions_map(self, models_that_changed: List[AindCoreModel]) -> Dict[AindCoreModel, str]:
         """
 
         Parameters
@@ -74,11 +96,24 @@ def _get_incremented_versions_map(models_that_changed: List[AindCoreModel]) -> D
 
         """
         version_bump_map = {}
-        # TODO: Use commit message to determine version number to bump?
         for model in models_that_changed:
+            # We only want to bump the patch if the major or minor versions didn't already change
+            # Load the current version of the model
+            original_schema = self._get_schema_json(model)
+            schema_version = original_schema.get("properties", {}).get("schema_version", {}).get("default")
+            if schema_version:
+                orig_ver = semver.Version.parse(schema_version)
+            else:
+                raise ValueError("Schema version not found in the schema file")
+
             old_v = semver.Version.parse(model.model_fields["schema_version"].default)
-            new_v = old_v.bump_patch()
-            version_bump_map[model] = str(new_v)
+            if orig_ver.major == old_v.major and orig_ver.minor == old_v.minor:
+                print(f"Updating {model.__name__} from {old_v} to {old_v.bump_patch()}")
+                new_ver = old_v.bump_patch()
+                version_bump_map[model] = str(new_ver)
+            else:
+                print(f"Skipping {model.__name__}, major or minor version already updated")
+                new_ver = old_v
         return version_bump_map
 
     @staticmethod
@@ -98,6 +133,7 @@ def _get_updated_file(python_file_path: str, new_ver: str) -> list:
 
         """
         new_file_contents = []
+        print(f"Updating {python_file_path} to version {new_ver}")
         with open(python_file_path, "rb") as f:
             file_lines = f.readlines()
         for line in file_lines:


Test patch:
diff --git a/tests/test_base.py b/tests/test_base.py
index 968630288..dd5b39f2f 100644
--- a/tests/test_base.py
+++ b/tests/test_base.py
@@ -1,6 +1,7 @@
 """ tests for Subject """
 
 import json
+import warnings
 import unittest
 from datetime import datetime, timezone
 from pathlib import Path
@@ -108,18 +109,17 @@ def test_aind_generic_constructor(self):
 
     def test_aind_generic_validate_fieldnames(self):
         """Tests that fieldnames are validated in AindGeneric"""
-        expected_error = "1 validation error for AindGeneric\n" "  Value error, Field names cannot contain '.' or '$' "
         invalid_params = [
             {"$foo": "bar"},
             {"foo": {"foo.name": "bar"}},
         ]
         for params in invalid_params:
-            with self.assertRaises(ValidationError) as e:
+            with warnings.catch_warnings(record=True) as w:
+                warnings.simplefilter("always")
                 AindGeneric(**params)
-            self.assertIn(expected_error, repr(e.exception))
-            with self.assertRaises(ValidationError) as e:
+                self.assertTrue(any("fields that contain '.' or '$'" in str(warning.message) for warning in w))
                 AindGeneric.model_validate(params)
-            self.assertIn(expected_error, repr(e.exception))
+                self.assertTrue(any("fields that contain '.' or '$'" in str(warning.message) for warning in w))
 
     def test_ccf_validator(self):
         """Tests that CCFStructure validator works"""
diff --git a/tests/test_bump_schema_versions.py b/tests/test_bump_schema_versions.py
index fec955bb1..8f1c2e542 100644
--- a/tests/test_bump_schema_versions.py
+++ b/tests/test_bump_schema_versions.py
@@ -8,6 +8,7 @@
 
 from aind_data_schema.core.session import Session
 from aind_data_schema.core.subject import Subject
+from aind_data_schema.core.rig import Rig
 from aind_data_schema.utils.json_writer import SchemaWriter
 from aind_data_schema.utils.schema_version_bump import SchemaVersionHandler
 
@@ -39,7 +40,8 @@ def test_get_list_of_models_that_changed(self, mock_exists: MagicMock, mock_json
         self.assertTrue(Session in models_that_changed)
         self.assertTrue(Subject in models_that_changed)
 
-    def test_get_list_of_incremented_versions(self):
+    @patch("aind_data_schema.utils.schema_version_bump.SchemaVersionHandler._get_schema_json")
+    def test_get_list_of_incremented_versions(self, mock_get_schema: MagicMock):
         """Tests get_list_of_incremented_versions method"""
 
         handler = SchemaVersionHandler(json_schemas_location=Path("."))
@@ -47,8 +49,16 @@ def test_get_list_of_incremented_versions(self):
         new_subject_version = str(Version.parse(old_subject_version).bump_patch())
         old_session_version = Session.model_fields["schema_version"].default
         new_session_version = str(Version.parse(old_session_version).bump_patch())
-        # Pycharm raises a warning about types that we can ignore
-        # noinspection PyTypeChecker
+
+        def side_effect(model):
+            """Side effect for mock_get_schema"""
+            if model == Subject:
+                return {"properties": {"schema_version": {"default": old_subject_version}}}
+            elif model == Session:
+                return {"properties": {"schema_version": {"default": old_session_version}}}
+
+        mock_get_schema.side_effect = side_effect
+
         model_map = handler._get_incremented_versions_map([Subject, Session])
         expected_model_map = {Subject: new_subject_version, Session: new_session_version}
         self.assertEqual(expected_model_map, model_map)
@@ -64,6 +74,58 @@ def test_write_new_file(self, mock_open: MagicMock):
         mock_open.assert_called_once_with(file_path, "wb")
         mock_open.return_value.__enter__().write.assert_has_calls([call(file_contents[0]), call(file_contents[1])])
 
+    @patch("builtins.open")
+    @patch("json.load")
+    @patch("pathlib.Path.exists")
+    def test_get_schema_json(self, mock_exists: MagicMock, mock_json_load: MagicMock, mock_open: MagicMock):
+        """Tests _get_schema_json method"""
+        handler = SchemaVersionHandler(json_schemas_location=Path("."))
+
+        mock_exists.return_value = True
+        mock_json_load.return_value = {"properties": {"schema_version": {"default": "1.0.0"}}}
+
+        model = MagicMock()
+        model.default_filename.return_value = "test_model.json"
+
+        schema_json = handler._get_schema_json(model)
+        self.assertEqual(schema_json, {"properties": {"schema_version": {"default": "1.0.0"}}})
+
+        mock_open.assert_called_once_with(Path("./test_model_schema.json"), "r")
+        mock_json_load.assert_called_once()
+
+    @patch("pathlib.Path.exists")
+    def test_get_schema_json_file_not_found(self, mock_exists: MagicMock):
+        """Tests _get_schema_json method when file is not found"""
+        handler = SchemaVersionHandler(json_schemas_location=Path("."))
+
+        mock_exists.return_value = False
+
+        model = MagicMock()
+        model.default_filename.return_value = "test_model.json"
+
+        with self.assertRaises(FileNotFoundError):
+            handler._get_schema_json(model)
+
+    @patch("aind_data_schema.utils.schema_version_bump.SchemaVersionHandler._get_schema_json")
+    def test_get_incremented_versions_map_exception(self, mock_get_schema: MagicMock):
+        """Test that missing schema_version field raises an error"""
+        handler = SchemaVersionHandler(json_schemas_location=Path("."))
+
+        mock_get_schema.return_value = {}
+
+        with self.assertRaises(ValueError):
+            handler._get_incremented_versions_map([Subject])
+
+    @patch("aind_data_schema.utils.schema_version_bump.SchemaVersionHandler._get_schema_json")
+    def test_get_incremented_versions_map_skip(self, mock_get_schema: MagicMock):
+        """Test that missing schema_version field raises an error"""
+        handler = SchemaVersionHandler(json_schemas_location=Path("."))
+
+        mock_get_schema.return_value = {"properties": {"schema_version": {"default": "0.0.0"}}}
+
+        empty_map = handler._get_incremented_versions_map([Subject])
+        self.assertEqual(empty_map, {})
+
     @patch("aind_data_schema.utils.schema_version_bump.SchemaVersionHandler._write_new_file")
     def test_update_files(self, mock_write: MagicMock):
         """Tests the update_files method"""
@@ -72,9 +134,11 @@ def test_update_files(self, mock_write: MagicMock):
         new_subject_version = str(Version.parse(old_subject_version).bump_patch())
         old_session_version = Session.model_fields["schema_version"].default
         new_session_version = str(Version.parse(old_session_version).bump_patch())
+        old_rig_version = Rig.model_fields["schema_version"].default
+        new_rig_version = str(Version.parse(old_rig_version).bump_minor())
         # Pycharm raises a warning about types that we can ignore
         # noinspection PyTypeChecker
-        handler._update_files({Subject: new_subject_version, Session: new_session_version})
+        handler._update_files({Subject: new_subject_version, Session: new_session_version, Rig: new_rig_version})
 
         expected_line_change0 = (
             f'schema_version: SkipValidation[Literal["{new_subject_version}"]] = Field(default="{new_subject_version}")'
@@ -90,16 +154,30 @@ def test_update_files(self, mock_write: MagicMock):
         self.assertTrue(expected_line_change1 in str(mock_write_args1[0]))
         self.assertTrue("session.py" in str(mock_write_args1[1]))
 
+    @patch("aind_data_schema.utils.schema_version_bump.SchemaVersionHandler._get_schema_json")
     @patch("aind_data_schema.utils.schema_version_bump.SchemaVersionHandler._get_list_of_models_that_changed")
     @patch("aind_data_schema.utils.schema_version_bump.SchemaVersionHandler._update_files")
-    def test_run_job(self, mock_update_files: MagicMock, mock_get_list_of_models: MagicMock):
+    def test_run_job(
+        self, mock_update_files: MagicMock, mock_get_list_of_models: MagicMock, mock_get_schema: MagicMock
+    ):
         """Tests run_job method"""
 
         old_subject_version = Subject.model_fields["schema_version"].default
         new_subject_version = str(Version.parse(old_subject_version).bump_patch())
         old_session_version = Session.model_fields["schema_version"].default
         new_session_version = str(Version.parse(old_session_version).bump_patch())
+
         mock_get_list_of_models.return_value = [Subject, Session]
+
+        def side_effect(model):
+            """Return values for get_schema_json"""
+            if model == Subject:
+                return {"properties": {"schema_version": {"default": old_subject_version}}}
+            elif model == Session:
+                return {"properties": {"schema_version": {"default": old_session_version}}}
+
+        mock_get_schema.side_effect = side_effect
+
         handler = SchemaVersionHandler(json_schemas_location=Path("."))
         handler.run_job()
         mock_update_files.assert_called_once_with({Subject: new_subject_version, Session: new_session_version})
diff --git a/tests/test_metadata.py b/tests/test_metadata.py
index 506ea3d96..45ef4a41c 100644
--- a/tests/test_metadata.py
+++ b/tests/test_metadata.py
@@ -4,7 +4,7 @@
 import re
 import unittest
 from datetime import datetime, time, timezone
-from unittest.mock import MagicMock, call, patch
+from unittest.mock import MagicMock, patch
 import uuid
 
 from aind_data_schema_models.modalities import Modality
@@ -418,12 +418,11 @@ def test_create_from_core_jsons(self):
         )
         expected_result = json.loads(expected_md.model_dump_json(by_alias=True))
         # there are some userwarnings when creating Subject from json
-        with self.assertWarns(UserWarning):
-            result = create_metadata_json(
-                name=self.sample_name,
-                location=self.sample_location,
-                core_jsons=core_jsons,
-            )
+        result = create_metadata_json(
+            name=self.sample_name,
+            location=self.sample_location,
+            core_jsons=core_jsons,
+        )
         # check that metadata was created with expected values
         self.assertEqual(self.sample_name, result["name"])
         self.assertEqual(self.sample_location, result["location"])
@@ -445,17 +444,15 @@ def test_create_from_core_jsons_optional_overwrite(self):
         external_links = {
             ExternalPlatforms.CODEOCEAN.value: ["123", "abc"],
         }
-        # there are some userwarnings when creating from json
-        with self.assertWarns(UserWarning):
-            result = create_metadata_json(
-                name=self.sample_name,
-                location=self.sample_location,
-                core_jsons={
-                    "subject": self.subject_json,
-                },
-                optional_created=created,
-                optional_external_links=external_links,
-            )
+        result = create_metadata_json(
+            name=self.sample_name,
+            location=self.sample_location,
+            core_jsons={
+                "subject": self.subject_json,
+            },
+            optional_created=created,
+            optional_external_links=external_links,
+        )
         self.assertEqual(self.sample_name, result["name"])
         self.assertEqual(self.sample_location, result["location"])
         self.assertEqual("2024-10-31T12:00:00Z", result["created"])
@@ -476,13 +473,11 @@ def test_create_from_core_jsons_invalid(self, mock_warning: MagicMock):
             "instrument": None,
             "quality_control": None,
         }
-        # there are some userwarnings when creating Subject from json
-        with self.assertWarns(UserWarning):
-            result = create_metadata_json(
-                name=self.sample_name,
-                location=self.sample_location,
-                core_jsons=core_jsons,
-            )
+        result = create_metadata_json(
+            name=self.sample_name,
+            location=self.sample_location,
+            core_jsons=core_jsons,
+        )
         # check that metadata was still created
         self.assertEqual(self.sample_name, result["name"])
         self.assertEqual(self.sample_location, result["location"])
@@ -496,47 +491,6 @@ def test_create_from_core_jsons_invalid(self, mock_warning: MagicMock):
         mock_warning.assert_called_once()
         self.assertIn("Issue with metadata construction!", mock_warning.call_args_list[0].args[0])
 
-    @patch("logging.warning")
-    @patch("aind_data_schema.core.metadata.is_dict_corrupt")
-    def test_create_from_core_jsons_corrupt(self, mock_is_dict_corrupt: MagicMock, mock_warning: MagicMock):
-        """Tests metadata json creation ignores corrupt core jsons"""
-        # mock corrupt procedures and processing
-        mock_is_dict_corrupt.side_effect = lambda x: (x == self.procedures_json or x == self.processing_json)
-        core_jsons = {
-            "subject": self.subject_json,
-            "data_description": None,
-            "procedures": self.procedures_json,
-            "session": None,
-            "rig": None,
-            "processing": self.processing_json,
-            "acquisition": None,
-            "instrument": None,
-            "quality_control": None,
-        }
-        # there are some userwarnings when creating Subject from json
-        with self.assertWarns(UserWarning):
-            result = create_metadata_json(
-                name=self.sample_name,
-                location=self.sample_location,
-                core_jsons=core_jsons,
-            )
-        # check that metadata was still created
-        self.assertEqual(self.sample_name, result["name"])
-        self.assertEqual(self.sample_location, result["location"])
-        self.assertEqual(self.subject_json, result["subject"])
-        self.assertIsNone(result["acquisition"])
-        self.assertEqual(MetadataStatus.VALID.value, result["metadata_status"])
-        # check that corrupt core jsons were ignored
-        self.assertIsNone(result["procedures"])
-        self.assertIsNone(result["processing"])
-        mock_warning.assert_has_calls(
-            [
-                call("Provided processing is corrupt! It will be ignored."),
-                call("Provided procedures is corrupt! It will be ignored."),
-            ],
-            any_order=True,
-        )
-
     def test_last_modified(self):
         """Test that the last_modified field enforces timezones"""
         m = Metadata.model_construct(
diff --git a/tests/test_rig_session_compatibility.py b/tests/test_rig_session_compatibility.py
index 666c90e9c..3e50bc287 100644
--- a/tests/test_rig_session_compatibility.py
+++ b/tests/test_rig_session_compatibility.py
@@ -10,6 +10,7 @@
 from aind_data_schema_models.organizations import Organization
 
 import aind_data_schema.components.devices as d
+from aind_data_schema.components.tile import Channel
 import aind_data_schema.core.rig as r
 from aind_data_schema.components.devices import (
     Calibration,
@@ -809,12 +810,32 @@ def read_json(filepath: Path) -> dict:
                             patch_cord_output_power=40,
                             output_power_unit="microwatt",
                             fiber_name="Fiber A",
+                            channel=Channel(
+                                channel_name="Channel A",
+                                intended_measurement="Dopamine",
+                                light_source_name="Laser A",
+                                filter_names=["Excitation filter 410nm"],
+                                detector_name="Green CMOS",
+                                excitation_wavelength=410,
+                                excitation_power=10,
+                                emission_wavelength=600,
+                            )
                         ),
                         FiberConnectionConfig(
                             patch_cord_name="Patch Cord B",
                             patch_cord_output_power=43,
                             output_power_unit="microwatt",
                             fiber_name="Fiber B",
+                            channel=Channel(
+                                channel_name="Channel B",
+                                intended_measurement="Dopamine",
+                                light_source_name="Laser B",
+                                filter_names=["Excitation filter 560nm"],
+                                detector_name="Red CMOS",
+                                excitation_wavelength=560,
+                                excitation_power=7,
+                                emission_wavelength=700,
+                            )
                         ),
                     ],
                     notes="Internal trigger. GRAB-DA2m shows signal. Unclear about GRAB-rAC",