This commit is contained in:
2026-04-17 14:55:32 -04:00
commit bc3ac1d4c9
18017 changed files with 4371742 additions and 0 deletions

View File

@@ -0,0 +1,51 @@
---
source_files:
- DataPRO/UnitTest/DTS.Common.DataModel.Tests/GroupShould.cs
generated_at: "2026-04-16T03:51:03.653495+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "574ef8550b69736d"
---
# DTS.Common.DataModel.Tests
## Documentation Page: `GroupShould.cs` Unit Tests
### 1. Purpose
This file contains unit tests for the `Group` class in the `GroupList.Model` namespace, specifically validating the behavior of its `Filter` method and `ConvertToEmbedded` method. The tests ensure that filtering logic correctly handles edge cases (null/empty search terms, missing associated test setups), searches across multiple properties (`DisplayName`, `Description`, `ChannelCount`, `LastModified`), and that `ConvertToEmbedded` behaves idempotently when invoked on an already-embedded group.
### 2. Public Interface
The tests reference the following public members of the `Group` class (defined externally, not in this file):
- **`bool Filter(string term)`**
Performs a case-sensitive substring search across `DisplayName`, `Description`, `ChannelCount` (as string), and `LastModified` (as string, using default `ToString()`). Returns `true` if any field contains the search term (or if `term` is null/empty, or if `AssociatedTestSetups` is null), otherwise `false`.
- **`void ConvertToEmbedded(IGroupChannel[] channels)`**
Converts the `Group` instance to an embedded state. Tests confirm that invoking this method on a group with `Embedded = true` does not cause an exception and leaves `Embedded` as `true`. (Full behavior of this method is not fully specified by the tests.)
### 3. Invariants
- `Filter("")` and `Filter(null)` **must** return `true`.
- If `AssociatedTestSetups` is `null`, `Filter(term)` **must** return `true` (regardless of `term`).
- `Filter(term)` returns `true` if `term` is a substring of:
- `DisplayName`,
- `Description`,
- `ChannelCount.ToString()`, or
- `LastModified.ToString()`
(using default string conversion and case-sensitive comparison).
- `Filter(term)` returns `false` only if `term` is non-null/non-empty, `AssociatedTestSetups` is non-null, and `term` is not found in any of the above fields.
- `ConvertToEmbedded` must be safe to call on an already-embedded group (no exception thrown, `Embedded` remains `true`).
### 4. Dependencies
- **Internal dependencies (inferred from test setup):**
- `GroupList.Model.Group` — the class under test.
- `Interface.Groups.GroupList.TestSetupParentHelper` — used to populate `AssociatedTestSetups` (a `List<TestSetupParentHelper>`).
- `Interface.Channels.IGroupChannel` — used as the parameter type for `ConvertToEmbedded`.
- **Test framework:** `NUnit` (v3+), via `[TestFixture]` and `[Test]` attributes.
- **Dependencies on this module:** None explicitly stated in this test file. Tests are internal to the `DTS.Common.DataModel.Tests` namespace.
### 5. Gotchas
- **Case sensitivity:** Tests do not verify case-insensitive matching; `Filter` appears to be case-sensitive (e.g., `"abc"` matches `"defabchy"` but not `"defABChy"` unless explicitly implemented otherwise — not confirmed here).
- **String conversion of `ChannelCount` and `LastModified`:** Uses default `.ToString()`; behavior may vary with culture settings (e.g., `LastModified.ToString()` may produce `"12/31/2023 10:00:00 AM"` vs `"2023-12-31T10:00:00"` depending on environment).
- **`AssociatedTestSetups` null vs empty list:** Tests distinguish between `null` (returns `true` for any term) and an empty list (returns `false` if term not found elsewhere).
- **`ConvertToEmbedded` behavior incomplete:** Tests only verify idempotency for already-embedded groups. No tests confirm behavior for non-embedded groups, side effects (e.g., channel assignment), or exception conditions.
- **No tests for `Embedded` property mutation:** While `Embedded` is read in tests, no tests assert that `ConvertToEmbedded` sets it to `true` when initially `false`.

View File

@@ -0,0 +1,43 @@
---
source_files:
- DataPRO/UnitTest/DTS.Common.DataModel.Tests/Properties/AssemblyInfo.cs
generated_at: "2026-04-16T03:51:32.946295+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "a096273db62ec51e"
---
# Properties
## 1. Purpose
This module is the test assembly for `DTS.Common.DataModel`, a data modeling component in the DTS (Data Transformation Services) system. Its purpose is to host unit tests that validate the correctness, reliability, and behavior of types defined in the `DTS.Common.DataModel` library. As a dedicated test project, it does not contribute runtime logic to the main application but ensures the integrity of core data structures and their associated operations through automated testing.
## 2. Public Interface
**No public API surface is exposed by this assembly.**
The file `AssemblyInfo.cs` contains only assembly-level metadata attributes (e.g., title, version, GUID, COM visibility). It defines no public classes, interfaces, methods, or properties. All attributes are standard .NET assembly attributes from `System.Reflection`, `System.Runtime.CompilerServices`, and `System.Runtime.InteropServices`.
## 3. Invariants
- The assembly is **not visible to COM** (`ComVisible(false)`).
- The assembly identity is fixed:
- `AssemblyTitle`: `"DTS.Common.DataModel.Tests"`
- `AssemblyVersion`: `"1.0.0.0"`
- `AssemblyFileVersion`: `"1.0.0.0"`
- `Guid`: `"1ffca92d-a1b0-4425-9074-7a1b91c04cb9"`
- The assembly culture is neutral (`AssemblyCulture("")`), indicating it is not satellite-locale-specific.
- No runtime behavior or stateful invariants apply—this is purely metadata.
## 4. Dependencies
- **Depends on**:
- `System.Runtime` (via `System.Runtime.CompilerServices`)
- `System.Runtime.InteropServices` (for COM interop attributes)
- `System.Reflection` (for reflection-based assembly attributes)
- **Depended on by**:
- None directly—this is a test assembly. It likely references testing frameworks (e.g., MSTest, xUnit, NUnit) and the `DTS.Common.DataModel` library under test, but those dependencies are not declared in *this* file.
- **Consumed by**:
- Test runners (e.g., `vstest.console.exe`, `dotnet test`) that load and execute tests from this assembly.
## 5. Gotchas
- **No test logic resides here**: Developers should not expect to find test methods or test fixtures in `AssemblyInfo.cs`. Actual tests are in other files (e.g., `*.cs` files in the same project directory, not provided here).
- **Version numbers are placeholders**: `AssemblyVersion("1.0.0.0")` and `AssemblyFileVersion("1.0.0.0")` are static stubs; they may need updating for releases but are not auto-generated (e.g., no `1.0.*` wildcard is active).
- **COM interop is disabled**: Setting `ComVisible(false)` is standard for .NET test projects but could cause issues if this assembly were ever intended for COM consumption (unlikely for a test project).
- **None identified from source alone** regarding behavioral quirks, tech debt, or non-obvious side effects—this file is purely declarative metadata.

View File

@@ -0,0 +1,111 @@
---
source_files:
- DataPRO/UnitTest/DTS.Common.Import.Tests/GroupHelperShould.cs
- DataPRO/UnitTest/DTS.Common.Import.Tests/CalibrationImportShould.cs
generated_at: "2026-04-16T03:51:24.502617+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "91da420b0b2120ea"
---
# DTS.Common.Import.Tests
## Documentation: `DTS.Common.Import.Tests` Unit Test Module
---
### 1. **Purpose**
This module contains unit tests for the `DTS.Common.Import` functionality, specifically validating the behavior of `GroupHelper` and `CalibrationImport` classes. Its role is to ensure correctness, robustness, and invariants of data import logic—particularly around group creation, channel ordering, sensor ID normalization, and calibration record handling—including edge cases such as null inputs and excitation voltage mismatches. It does not contain production logic itself but serves as the authoritative specification for expected behavior of the tested components.
---
### 2. **Public Interface**
The test file itself does not expose a public API; it *tests* public members of other modules. Based on test usage, the following public APIs are referenced:
#### `GroupHelper.CreateEmptyGroup()`
- **Signature**: `static Group CreateEmptyGroup()` *(inferred from usage)*
- **Behavior**: Creates a new `Group` instance with `LastModifiedBy` initialized to `"---"`.
#### `GroupHelper.ReverseChannelOrder(TestTemplate, Dictionary<string, string>, List<SensorData>)`
- **Signature**: `static List<T> ReverseChannelOrder<T>(TestTemplate testTemplate, Dictionary<string, string> channelMap, List<SensorData> sensors)` *(inferred from usage)*
- **Behavior**: Safely handles null inputs for `testTemplate` and `sensors` by returning an empty list. Otherwise, performs channel reordering based on `channelMap`. *(Exact implementation details not visible.)*
#### `GroupHelper.NormalizeSensorIds(List<SensorData>)`
- **Signature**: `static List<SensorData> NormalizeSensorIds(List<SensorData> sensors)` *(inferred from usage)*
- **Behavior**: Safely handles null input by returning an empty list. Otherwise, normalizes sensor IDs in the list. *(Exact normalization logic not visible.)*
#### `CalibrationImport.CheckForExcitationCalibration(SensorCalibration, int, ExcitationVoltageOptions.ExcitationVoltageOption, string)`
- **Signature**: `SensorCalibration CheckForExcitationCalibration(SensorCalibration sensorCalibration, int channelIndex, ExcitationVoltageOptions.ExcitationVoltageOption expectedExcitation, string channelName)`
- **Behavior**:
- If the excitation voltage of the *first* calibration record matches `expectedExcitation`, returns the original `sensorCalibration` unchanged.
- If it does *not* match, returns a *modified* `SensorCalibration` with an additional calibration record (resulting in `Records.Records.Count == 2`). *(Exact record duplication or cloning logic not visible.)*
#### `CalibrationImport.AddLinearCalRecordIfNeeded(SensorCalibration, bool, bool)`
- **Signature**: `SensorCalibration AddLinearCalRecordIfNeeded(SensorCalibration sensorCalibration, bool forceAdd, bool isZeroMethodRequired)` *(inferred from usage)*
- **Behavior**:
- If `sensorCalibration.Records.Records.Count != 1`, returns the original unchanged.
- If `Count == 1`, returns a modified `SensorCalibration` where:
- The existing records `Poly.IsValid` is set to `false`.
- A *second* calibration record is added (resulting in `Count == 2`).
*(Exact record creation logic not visible.)*
#### `CalibrationImport.AddLinearZeroMethodIfNeeded(SensorCalibration, ZeroMethodType, int, int)`
- **Signature**: `SensorCalibration AddLinearZeroMethodIfNeeded(SensorCalibration sensorCalibration, ZeroMethodType zeroMethodType, int channelIndex, int sensorIndex)` *(inferred from usage)*
- **Behavior**:
- If `sensorCalibration.Records.Records.Count != 1`, returns the original unchanged.
- If `Count == 1`, returns a modified `SensorCalibration` with `ZeroMethods.Methods.Count == 2`. *(Exact zero method addition logic not visible.)*
---
### 3. **Invariants**
- `GroupHelper.CreateEmptyGroup()` always produces a `Group` with `LastModifiedBy == "---"`.
- `GroupHelper.ReverseChannelOrder()` and `GroupHelper.NormalizeSensorIds()` must never throw exceptions when passed `null` for `sensors` or `testTemplate`; they must return empty lists instead.
- `CalibrationImport.CheckForExcitationCalibration()`:
- When the first calibration records `Excitation` matches the expected value, the returned object is *identical* (reference equality not tested, but no modification is performed).
- When mismatched, the returned `SensorCalibration` has exactly *two* calibration records.
- `CalibrationImport.AddLinearCalRecordIfNeeded()`:
- Only modifies the calibration when there is exactly *one* record.
- In that case, it invalidates the existing record (`Poly.IsValid == false`) and adds a second record.
- `CalibrationImport.AddLinearZeroMethodIfNeeded()`:
- Only modifies the calibration when there is exactly *one* record.
- In that case, it ensures `ZeroMethods.Methods.Count == 2`.
---
### 4. **Dependencies**
**Test Module Dependencies**:
- `DTS.Common.Import.Tests` references:
- `DTS.Common.Interface.TestSetups.TestSetupsList` (for `TestTemplate`, inferred)
- `DTS.SensorDB` (for `SensorData`)
- `DataPROWin7.DataModel` (for `Group`, inferred)
- `NUnit.Framework` (test framework)
- `NSubstitute` (mocking library)
- `DTS.Common.Enums` (for `ExcitationVoltageOptions`, `ZeroMethodType`)
- `DTS.Common.Interface.Sensors` (for `ICalibrationRecords`, `ICalibrationRecord`)
- `Classes.Sensors.LinearizationFormula` (from `DTS.SensorDB` or `DataPROWin7.DataModel`, inferred)
**Tested Components Dependencies** *(inferred)*:
- `GroupHelper`, `CalibrationImport` likely depend on:
- `DTS.SensorDB` (sensor data models)
- `DTS.Common.Interface.Sensors` (calibration interfaces)
- `DTS.Common.Enums` (enum types)
- Possibly `DataPROWin7.DataModel` (e.g., `Group`, `TestTemplate`)
**Dependents**:
- This test module is consumed by the test runner (NUnit) and likely part of a CI pipeline. No runtime dependents are visible.
---
### 5. **Gotchas**
- **Typos in test names**: Tests use `"ShouldNotThorwException"` (missing 'w' in "Throw")—likely a typo in test method names, but does not affect behavior.
- **Ambiguity in record duplication logic**: In `CheckForExcitationCalibration`, when excitation mismatches, a second record is added—but it is unclear whether this is a *copy* of the first record, a *new placeholder*, or a *modified* record. The tests only assert count, not content.
- **Zero method vs. calibration record distinction**: `AddLinearZeroMethodIfNeeded` modifies `ZeroMethods.Methods`, while `AddLinearCalRecordIfNeeded` modifies `Records.Records`. The interaction between these two mechanisms is not tested—potential for confusion or overlap.
- **No tests for `AddLinearCalRecordIfNeeded` with `forceAdd == true` or `isZeroMethodRequired == true`**: Only default (`false`, `false`) cases are tested.
- **No tests for non-null, non-empty inputs in `GroupHelper` methods**: All tests for `ReverseChannelOrder` and `NormalizeSensorIds` only verify null-safety; behavior on valid inputs is not specified here.
> *No other obvious tech debt or quirks identified from source alone.*

View File

@@ -0,0 +1,38 @@
---
source_files:
- DataPRO/UnitTest/DTS.Common.Import.Tests/Properties/AssemblyInfo.cs
generated_at: "2026-04-16T03:51:40.727967+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "61dda13889e17e57"
---
# Properties
## 1. Purpose
This module is a unit test assembly (`DTS.Common.Import.Tests`) for the `DTS.Common.Import` component. Its sole purpose is to house and execute automated tests to validate the correctness, reliability, and behavior of the import-related functionality in the `DTS.Common` library. It is not a runtime component but a development-time artifact used for regression prevention and quality assurance during the software development lifecycle.
## 2. Public Interface
**No public API surface is exposed.**
The file contains only assembly-level attributes (via `System.Reflection` and `System.Runtime.InteropServices`) and no types (classes, structs, interfaces, etc.) or methods. Therefore, there are no public functions, classes, or methods documented here.
## 3. Invariants
- The assembly identity is fixed at version `1.0.0.0` (both `AssemblyVersion` and `AssemblyFileVersion`).
- The assembly is non-`COM`-visible (`ComVisible(false)`), meaning its types are not exposed to COM clients.
- The GUID `38a471f6-c94f-4ae1-8ef5-d49f309bb3c2` uniquely identifies the typelib for COM interop scenarios (though irrelevant here due to `ComVisible(false)`).
- The assembly name is `DTS.Common.Import.Tests`, indicating its role as a test container.
## 4. Dependencies
- **Build-time dependencies**:
- `System.Reflection`, `System.Runtime.CompilerServices`, `System.Runtime.InteropServices` (standard .NET namespaces for assembly metadata).
- **Implicit runtime dependencies**:
- The `DTS.Common.Import` assembly (or its dependencies), since this is a test project—though not directly referenced in *this* file, it is implied by the project name and typical test project structure.
- **Consumers**:
- Test runners (e.g., MSTest, NUnit, xUnit) that discover and execute tests defined in this assembly.
- CI/CD pipelines or local development environments that invoke unit tests for the `DTS.Common.Import` module.
## 5. Gotchas
- **No logic or test code is present in this file**—it only defines assembly metadata. Actual test implementations reside elsewhere (e.g., in `.cs` files in the same project, not provided here).
- The `AssemblyVersion("1.0.0.0")` and `AssemblyFileVersion("1.0.0.0")` are hardcoded and may not reflect actual release versions unless updated externally (e.g., via build scripts or CI).
- The `AssemblyConfiguration` and `AssemblyCompany` attributes are empty strings, suggesting minimal build-time customization or metadata injection.
- **None identified from source alone.**

View File

@@ -0,0 +1,147 @@
---
source_files:
- DataPRO/UnitTest/DatabaseUnitTesting/DefaultPropertiesTests.cs
- DataPRO/UnitTest/DatabaseUnitTesting/LaboratoryDetailsTests.cs
- DataPRO/UnitTest/DatabaseUnitTesting/TestSetups.cs
- DataPRO/UnitTest/DatabaseUnitTesting/ResultSetTester.cs
- DataPRO/UnitTest/DatabaseUnitTesting/DASTests.cs
- DataPRO/UnitTest/DatabaseUnitTesting/CustomerDetailsTests.cs
- DataPRO/UnitTest/DatabaseUnitTesting/DASChannelsTests.cs
- DataPRO/UnitTest/DatabaseUnitTesting/CalculatedChannelsTests.cs
- DataPRO/UnitTest/DatabaseUnitTesting/DBAPITests.cs
- DataPRO/UnitTest/DatabaseUnitTesting/DbAPITestsRegionsOfInterest.cs
- DataPRO/UnitTest/DatabaseUnitTesting/DatabaseModificationTester.cs
- DataPRO/UnitTest/DatabaseUnitTesting/DbAPITestsChannels.cs
- DataPRO/UnitTest/DatabaseUnitTesting/DbAPITestsSensorsAnalog.cs
- DataPRO/UnitTest/DatabaseUnitTesting/DbAPITestsGroupHardware.cs
- DataPRO/UnitTest/DatabaseUnitTesting/DbAPITestsCustomerDetails.cs
generated_at: "2026-04-16T03:51:39.334243+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "f699b3e38a2dc576"
---
# Database Unit Testing Module Documentation
## 1. Purpose
This module provides a structured framework for database unit testing within the DataPRO system, using NUnit to validate database interactions via stored procedures and the `DbAPI` interface. It ensures data integrity and correct behavior of database operations (insert, update, delete, get) for core entities—such as DAS, DAS channels, laboratory details, customer details, calculated channels, and regions of interest—by leveraging database snapshots and transactional rollbacks to maintain test isolation and repeatability. The module supports two distinct testing approaches: low-level stored procedure testing (via `ResultSetTester` and `DatabaseModificationTester`) and high-level API testing (via `DbAPI` methods), enabling comprehensive coverage of database functionality.
## 2. Public Interface
### TestSetups (Base Class for Test Fixtures)
- **`[OneTimeSetUp] TFSetup()`**
Initializes a `SqlConnection`, opens it, and instantiates `DatabaseModificationTester`, which creates a database snapshot for test isolation.
- **`[OneTimeTearDown] TFTearDown()`**
Disposes `DatabaseModificationTester` (dropping the snapshot) and closes the connection; does *not* delete test data.
- **`[SetUp] SetUp()`**
Begins a new transaction via `DatabaseModificationTester.BeginTestTransaction()`, sets up a `SqlCommand` bound to the transaction.
- **`[TearDown] TearDown()`**
Rolls back the current test transaction via `DatabaseModificationTester.EndTestTransaction()`.
- **Properties**
- `TestResultPath`: Returns path to test results directory (`..\..\TestResults\` relative to app base directory).
- `UnitTests`: Exposes the `DatabaseModificationTester` instance.
- `BSETUPMODE`: Always `false` (hardcoded constant).
- `Command`: Returns the `SqlCommand` bound to the current transaction.
### ResultSetTester
- **`ResultSetTester(SqlConnection connection, string procedureName)`**
Constructs a `SqlCommand` for the given stored procedure name, with `CommandType.StoredProcedure`.
- **`ResultSetTester(SqlCommand command)`**
Wraps an existing `SqlCommand`.
- **`SetInputParameter(string parameterName, object parameterValue)`**
Adds or replaces an input parameter on the command.
- **`SetOutputParameter(string parameterName, SqlDbType type, int size)`**
Adds or replaces an output parameter on the command, setting its direction to `Output`.
- **`PrintOuputParameterValues()`**
Returns an `IEnumerable<KeyValuePair<string, object>>` of all output parameter names and values.
- **`CompareToFile(string filename)`**
Parses the stored procedure result set using `ResultSetParser`, reads expected results from XML file via `XmlFileAdapter`, and returns `true` if equal.
- **`OutputToFile(string filename)`**
Parses the stored procedure result set and writes it to an XML file via `XmlFileAdapter`.
### DatabaseModificationTester
- **`DatabaseModificationTester(SqlConnection connection, string databaseName, string snapshotName)`**
Initializes a snapshot-based testing environment; creates a database snapshot on construction.
- **`BeginTestTransaction()`**
Begins a new transaction on the connection; throws if a transaction already exists (rolls back first).
- **`EndTestTransaction()`**
Rolls back and disposes the current transaction; calls `CleanUp()` internally.
- **`WriteDiffsToXml(string filename)`**
Generates differences between current transaction and the snapshot, writes to XML file.
- **`CompareDiffsToXml(string filename)`**
Compares current transaction differences to expected XML file; returns `true` if equal.
- **`AreEqual()`**
Returns `true` if no differences exist between current transaction and snapshot.
- **`AddColumnToIgnore(string schemaName, string objectName, string columnName)`**
Adds a single column to the ignore list for comparisons.
- **`AddColumnsToIgnore(string schema1, string name1, List<string> columnNames)`**
Adds multiple columns to the ignore list for a given table.
- **`AddObjectComparison(string schemaName, string tableName)`**
Registers a table for comparison (likely default behavior).
- **`Dispose()`**
Drops the database snapshot if active.
### DbAPI Tests (Partial Classes)
The `DbAPITests` partial class and its companions (`DbAPITestsRegionsOfInterest`, `DbAPITestsChannels`, `DbAPITestsSensorsAnalog`, `DbAPITestsGroupHardware`, `DbAPITestsCustomerDetails`) contain integration tests for the `DbAPI` layer. Key methods include:
- **`TestConnect()` / `TestLogin()`**
Verify database connectivity and user authentication.
- **`RegionsOfInterest()`**
Tests versioned ROI functionality (`sp_TestSetupsUpdateInsert_92`, `sp_TestSetupROIsInsert`, `sp_ROIPeriodChannelsInsert`, etc.), including graceful handling of pre-Version 92 databases.
- **`TestChannelsInsert()` / `TestChannelsUpdate()` / `TestChannelsGet()` / `TestChannelsDelete()`**
Validate channel CRUD operations via `DbAPI.Channels.*` methods.
- **`TestSensorsDeleteAll()` / `TestSensorsAnalogInsertAndDelete()` / `TestSensorAnalogInsertAndDeleteShouldFail()` / `TestSensorsAnalogBridgeResistanceGet()`**
Validate analog sensor operations, including error handling for null user/connection and special test sensors that persist after bulk delete.
- **`TestGroupHardwareInsertGetAndDelete()`**
Validates group-hardware associations (`GroupHardware` table) including multi-DAS/group scenarios.
- **`CustomerDetails()`**
Validates customer details CRUD, including `null`-name delete (delete all) and name-based lookup.
## 3. Invariants
- **Test Isolation**: Each test runs inside a transaction that is rolled back after the test (`BeginTestTransaction`/`EndTestTransaction`), ensuring no persistent side effects.
- **Snapshot Consistency**: A database snapshot is created once per test fixture (`[OneTimeSetUp]`) and dropped at teardown (`[OneTimeTearDown]`), providing a known baseline for all tests in the fixture.
- **No Test Data Cleanup**: The teardown explicitly *skips* deletion of test data; only the transaction is rolled back and snapshot dropped.
- **Transaction Lifecycle**: A transaction must exist before calling `EndTestTransaction()`; otherwise, `InvalidOperationException` is thrown.
- **Output Parameter Handling**: `ResultSetTester.SetOutputParameter()` removes any existing parameter with the same name before adding a new one.
- **Versioned ROI Support**: ROI tests handle both pre-Version 92 (string-based) and Version 92+ (normalized tables) databases; `RegionsOfInterestInsert` is expected to fail on Version ≤91.
## 4. Dependencies
### Internal Dependencies
- **`DatabaseUnitTesting.Utilities` namespace**:
- `DatabaseComparer`, `DatabaseAdapter`, `ResultSetParser`, `XmlFileAdapter` (used by `DatabaseModificationTester` and `ResultSetTester`).
- **`DatabaseUnitTesting.Utilities.Results` namespace**:
- `Database` (used by `ResultSetParser` and `XmlFileAdapter`).
- **`DTS.Common.*` namespaces**:
- `DTS.Common.Interface.Database`, `DTS.Common.Interface.TestSetups`, `DTS.Common.Interface.Channels`, `DTS.Common.Interface.Sensors`, `DTS.Common.Interface.Groups`, `DTS.Common.Interface.TestMetaData`, `DTS.Common.Classes.*`, `DTS.Common.Enums.*`, `DTS.Common.Storage` (used by `DbAPITests*` classes).
- **`Properties.Settings.Default`**:
- Used for `ConnectionString`, `DBName`, `DBSnapshot` in `TestSetups`.
### External Dependencies
- **NUnit**: Testing framework (`[TestFixture]`, `[Test]`, `[SetUp]`, `[TearDown]`, `[OneTimeSetUp]`, `[OneTimeTearDown]`, `Assert.*`).
- **System.Data.SqlClient**: `SqlConnection`, `SqlCommand`, `SqlTransaction`, `SqlDbType`, `ParameterDirection`.
- **System.Data**: `DataTable`, `IDataReader`, `DbDataAdapter` (used via `ResultSetParser`).
- **LocalDB / SQL Server**: Target database engine; connection via `LocalDb` (`(localdb)\DataPROInstance`).
- **External Scripts**: `AttachDBs.bat`, `db` folder (used by `DbAPI.Connections.ConnectToDb`).
### Inferred Usage
- All test fixture classes (`DefaultPropertiesTests`, `LaboratoryDetailsTests`, `DASTests`, etc.) inherit from `TestSetups`.
- `ResultSetTester` is used by tests to execute stored procedures and compare result sets.
- `DatabaseModificationTester` is used by tests to assert database state changes (via `CompareDiffsToXml`, `AreEqual`, etc.).
- `DbAPI` is used for integration tests against the high-level API layer.
## 5. Gotchas
- **`BSETUPMODE` is Always `false`**: The constant `_BSETUPMODE = false` is hardcoded and never configurable; tests cannot switch to a setup mode.
- **Transaction Reuse Risk**: `BeginTestTransaction()` throws if a transaction already exists; tests must not call `BeginTestTransaction()` multiple times per test.
- **Snapshot Cleanup**: The snapshot is dropped only in `DatabaseModificationTester.Dispose()`, which is called in `[OneTimeTearDown]`. If tests are run in parallel, snapshot conflicts may occur.
- **Output Parameter Overwrite**: `ResultSetTester.SetOutputParameter()` removes existing parameters with the same name before adding a new one—this may mask bugs if parameter names are reused unintentionally.
- **`WriteDiffsToXml` Generates Manual Verification Files**: The XML files produced by `WriteDiffsToXml` must be manually reviewed and accepted as expected results; they are not auto-validated.
- **`CustomerDetails.Name` is Required**: Tests and comments indicate `Name` is the identifying field for insert/update; `null` may cause errors.
- **`DAS` Requires `SerialNumber`**: Tests and comments indicate `SerialNumber` is mandatory for DAS operations.
- **`SensorsDeleteAll` Preserves Test Sensors**: `TestSensorsDeleteAll` expects that specific test sensors (`SensorConstants.TEST_SPECIFIC_ANALOG_SERIAL`, `SensorConstants.TEST_SPECIFIC_CLOCK_SERIAL`) persist after a bulk delete.
- **ROI Version Handling**: `RegionsOfInterestInsert` is expected to fail (non-zero `hr`) on databases with version ≤91; tests must check version before invoking.
- **`DbAPI` Error Codes**: Return codes (`hr`) are used for success/failure (`0` = success); tests rely on this convention rather than exceptions.
- **Path Resolution Complexity**: `DbAPITests.Setup()` includes complex logic to locate `AttachDBs.bat` and `db` folder; behavior may vary depending on build configuration (e.g., Debug/Release, x86/x64).

View File

@@ -0,0 +1,50 @@
---
source_files:
- DataPRO/UnitTest/DatabaseUnitTesting/Properties/Settings.Designer.cs
generated_at: "2026-04-16T03:51:52.660365+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "f8ee505a9bbaa788"
---
# Properties
## 1. Purpose
This module defines strongly-typed application and user settings for the `DatabaseUnitTesting` project, specifically managing database connection and naming configuration used during unit testing. It leverages .NETs `ApplicationSettingsBase` to provide compile-time-safe access to configuration values—such as the test database connection string, database name, and snapshot name—enabling consistent and maintainable test environment setup without hardcoding values.
## 2. Public Interface
The class `Settings` (fully qualified: `DatabaseUnitTesting.Properties.Settings`) exposes the following members:
- **`public static Settings Default { get; }`**
A thread-safe singleton accessor for the settings instance. Returns the single shared `Settings` object used throughout the application.
- **`public string ConnectionString { get; }`**
An *application-scoped* read-only string property providing the SQL Server connection string for the unit test database. Default value:
`"Data Source=FAJITA\\DEV_SQL;Initial Catalog=DataPRO_UnitTest;Integrated Security=False;Persist Security Info=True;User ID=sa;Password=!!QQAAZZxxssww22;Connect Timeout=15;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False"`
Marked with `SpecialSettingAttribute.SpecialSetting.ConnectionString`, indicating it is intended for ADO.NET connection usage.
- **`public string DBName { get; set; }`**
A *user-scoped* read-write string property for the target database name used in tests. Default value: `"DataPRO_UnitTest"`.
- **`public string DBSnapshot { get; set; }`**
A *user-scoped* read-write string property for the database snapshot name used during testing (likely for restoring state between test runs). Default value: `"DataPRO_UnitTestSnapshot"`.
## 3. Invariants
- The `ConnectionString` property is **application-scoped** and immutable at runtime—its value cannot be changed programmatically after initialization.
- `DBName` and `DBSnapshot` are **user-scoped** and mutable; changes persist only for the current user and require explicit saving (e.g., via `Settings.Default.Save()`) to be persisted across sessions.
- The `Default` property returns a synchronized (thread-safe) instance, implying safe concurrent read access.
- The `ConnectionString` value is validated at design time by the Settings Designer as a well-formed connection string, but no runtime validation is present in this file.
## 4. Dependencies
- **Depends on**:
- `System.Configuration` (for `ApplicationSettingsBase`, attributes like `ApplicationScopedSettingAttribute`, `SpecialSettingAttribute`, etc.)
- `System.Diagnostics` (for `DebuggerNonUserCodeAttribute`)
- `System.Runtime.CompilerServices` (for `CompilerGeneratedAttribute`)
- **Used by**: Any code in the `DatabaseUnitTesting` project (or referencing it) that needs to access test database configuration—e.g., test setup/teardown logic, database initialization utilities, or test harnesses that restore snapshots or connect to the test database.
## 5. Gotchas
- **Hardcoded credentials**: The default `ConnectionString` embeds a plaintext password (`User ID=sa;Password=!!QQAAZZxxssww22`). This is a security risk and should be replaced with secure credential storage (e.g., Windows Credential Manager, Azure Key Vault) in production or shared environments.
- **Assumed SQL Server instance**: The `Data Source=FAJITA\DEV_SQL` implies a specific named instance; this will fail on machines without that instance unless overridden in user settings or config.
- **`Integrated Security=False`**: Explicit use of SQL authentication (not Windows auth) may not align with security policies in some environments.
- **No validation logic**: The settings class does not validate that `DBName` or `DBSnapshot` refer to actual databases or that `DBSnapshot` is a valid snapshot name. Errors will surface only at runtime during database operations.
- **Auto-generated file**: As indicated by the `<auto-generated>` header, manual edits will be overwritten on regeneration (e.g., after modifying settings in Visual Studios designer).

View File

@@ -0,0 +1,185 @@
---
source_files:
- DataPRO/UnitTest/DatabaseUnitTesting/Utilities/ObjectsToCompare.cs
- DataPRO/UnitTest/DatabaseUnitTesting/Utilities/DatabaseAdapter.cs
- DataPRO/UnitTest/DatabaseUnitTesting/Utilities/DatabaseComparer.cs
generated_at: "2026-04-16T03:52:03.248634+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "b192c520f6919014"
---
# Utilities
## Documentation: `DatabaseUnitTesting.Utilities` Module
---
### 1. Purpose
This module provides core infrastructure for comparing database schemas and data between two SQL Server databases as part of unit testing workflows. It enables programmatic definition of object pairs (e.g., tables or views) to compare, dynamic column exclusion, and execution of set-based data difference queries using SQL `EXCEPT`. The module is internal to the unit testing framework and is not intended for general-purpose database access; its primary consumers are test runners that validate expected data consistency across environments (e.g., baseline vs. modified database states).
---
### 2. Public Interface
> **Note**: All types and members are `internal`, not `public`. They are accessible only within the `DatabaseUnitTesting` assembly.
#### `ObjectsToCompare` class
Represents a pair of database objects (e.g., tables) to compare, with optional column exclusions.
- **Constructor**
```csharp
internal ObjectsToCompare(string schema1Name, string object1Name, string schema2Name, string object2Name)
```
Initializes a comparison pair. All arguments are non-null strings identifying the schema and object name for each side of the comparison.
- **Properties**
```csharp
internal string Schema1Name { get; }
internal string Object1Name { get; }
internal string Schema2Name { get; }
internal string Object2Name { get; }
internal string Qualified1 { get; } // Returns "schema1.object1"
internal string Qualified2 { get; } // Returns "schema2.object2"
internal IEnumerable<string> ColumnsToIgnore { get; }
```
- **Methods**
```csharp
internal void AddColumnToIgnore(string columnName)
```
Adds a column name (case-insensitive) to the list of columns to exclude from comparison.
---
#### `DatabaseAdapter` class
Encapsulates low-level SQL Server operations for snapshot management and database switching.
- **Constructor**
```csharp
internal DatabaseAdapter(SqlConnection connection)
```
Wraps an existing `SqlConnection`. Automatically opens the connection if closed.
- **Methods**
```csharp
internal bool IsSnapshot(string name)
```
Returns `true` if `name` refers to a database snapshot (checks `sys.databases.source_database_id`); `false` otherwise.
```csharp
internal void UseDatabase(string databaseName)
```
Executes `USE [databaseName]` on the connection.
```csharp
internal void CreateSnapshot(string databaseName, string snapshotName)
```
Creates a snapshot of `databaseName` at a fixed path: `C:\Temp\{snapshotName}`.
⚠️ **Hardcoded path**: Assumes `C:\Temp\` exists and is writable.
```csharp
internal void DropSnapshot(string snapshotName)
```
Drops the snapshot `snapshotName`. Throws `ArgumentException` if no such snapshot exists.
---
#### `DatabaseComparer` class
Orchestrates comparison of database objects between two databases.
- **Constructor**
```csharp
internal DatabaseComparer(SqlConnection connection, string databaseOneName, string databaseTwoName)
```
Initializes the comparer. Switches the connection context to `databaseOneName` via `DatabaseAdapter.UseDatabase`.
- **Properties & Fields**
- `_connection`: Underlying `SqlConnection`.
- `_databaseAdapter`: Internal `DatabaseAdapter` instance.
- `_databaseOne`, `_databaseTwo`: Names of the two databases being compared.
- **Methods**
```csharp
internal void CleanUp()
```
Clears the list of registered object comparisons (`_objectsToCompare`).
```csharp
internal void AddObjectComparison(string schema1, string object1, string schema2, string object2)
```
Registers a new object pair for comparison by creating and adding an `ObjectsToCompare` instance.
```csharp
internal void AddColumnToIgnore(string schemaName, string objectName, string columnName)
```
Adds a column to ignore for the first object matching `(schemaName, objectName)`.
⚠️ **Assumes uniqueness**: Uses `List.Find`, so only the *first* match is updated.
```csharp
internal void AddColumnsToIgnore(string schemaName, string tableName, List<string> columnNames)
```
Adds multiple columns to ignore for the same object.
```csharp
internal string GetAllColumns(SqlTransaction transaction, ObjectsToCompare objects)
```
Queries `INFORMATION_SCHEMA.COLUMNS` for `objects.Qualified1` in `_databaseOne`.
- Validates that the object exists (throws `ArgumentException` if not).
- Removes columns listed in `objects.ColumnsToIgnore`.
- Throws `ArgumentException` if a specified column is not found or if all columns are ignored.
```csharp
internal Table RunCompare(SqlTransaction transaction, ObjectsToCompare objectComparison)
```
Executes a data comparison between `objectComparison.Qualified1` and `objectComparison.Qualified2`.
- Uses `SELECT ... EXCEPT SELECT ...` to find rows present in one side but not the other.
- Adds a `TempRowNumber` column (via `ROW_NUMBER() OVER(PARTITION BY ... ORDER BY @@SPID)`) to handle duplicates.
- Iterates over result sets (first = "In First", second = "In Second") to populate a `Table` object.
- Column names are normalized to lowercase in the resulting `Column` objects.
```csharp
internal Database GenerateDifferences(SqlTransaction transaction)
```
Compares all registered objects and returns a `Database` object containing non-empty `Table` results.
- Skips tables with zero rows in the result set.
---
### 3. Invariants
- **Object existence**: `GetAllColumns` requires that the first object (`objects.Qualified1`) exists in `_databaseOne`; otherwise, it throws `ArgumentException`.
- **Column validation**: All columns passed to `AddColumnToIgnore` must exist in the target objects column list; otherwise, `GetAllColumns` throws `ArgumentException`.
- **Non-empty comparison**: At least one column must remain after ignoring columns; otherwise, `GetAllColumns` throws `ArgumentException`.
- **Snapshot path**: `CreateSnapshot` assumes `C:\Temp\` is a valid, writable directory.
- **Transaction scope**: All comparison queries (`RunCompare`, `GetAllColumns`) require an active `SqlTransaction` passed explicitly.
- **Case sensitivity**: Column names in `Column` objects are stored in lowercase, regardless of source casing.
---
### 4. Dependencies
- **Internal dependencies**:
- `System.Data`, `System.Data.SqlClient`: For `SqlConnection`, `SqlCommand`, `SqlTransaction`, `SqlDataReader`.
- `DatabaseUnitTesting.Utilities.Results`: Uses `Database`, `Table`, `Row`, and `Column` types (not included in source).
- **External dependencies**:
- SQL Server (tested against versions supporting `sys.databases.source_database_id`, `INFORMATION_SCHEMA.COLUMNS`, and `EXCEPT`).
- File system access to `C:\Temp\` for snapshot creation.
- **Consumers** (inferred):
- Unit test classes (e.g., `Microsoft.VisualStudio.TestTools.UnitTesting`-based tests) that use `DatabaseComparer` to assert data equality.
---
### 5. Gotchas
- **Hardcoded snapshot path**: `CreateSnapshot` uses `C:\Temp\` unconditionally. This will fail on non-Windows systems or if the directory is missing/readonly.
- **Ambiguous object matching**: `AddColumnToIgnore` and `AddColumnsToIgnore` use `List.Find`, which only updates the *first* matching `ObjectsToCompare` entry. If multiple entries share the same `(schema1, object1)`, only the first will be modified.
- **Case normalization**: Column names are lowercased in `Row.AddColumn(...)`, which may cause mismatches if downstream consumers expect original casing.
- **`@@SPID` ordering**: The `ORDER BY @@SPID` in `ROW_NUMBER()` is arbitrary and non-deterministic. It is used solely to generate distinct `TempRowNumber` values for duplicate rows; it does not imply stable ordering.
- **No schema validation**: `DatabaseComparer` does not verify that `object2` exists in `_databaseTwo`; it assumes the object exists and will fail at query time if not.
- **Transaction dependency**: All public comparison methods require a `SqlTransaction`. Using them outside a transaction context will cause runtime errors.
- **No cleanup of snapshots**: `DatabaseAdapter` provides `DropSnapshot`, but no automatic cleanup is performed by `DatabaseComparer` or `ObjectsToCompare`. Tests must explicitly manage snapshot lifecycle.
None identified beyond the above.

View File

@@ -0,0 +1,143 @@
---
source_files:
- DataPRO/UnitTest/DatabaseUnitTesting/Utilities/Results/Row.cs
- DataPRO/UnitTest/DatabaseUnitTesting/Utilities/Results/Column.cs
- DataPRO/UnitTest/DatabaseUnitTesting/Utilities/Results/Database.cs
- DataPRO/UnitTest/DatabaseUnitTesting/Utilities/Results/Table.cs
- DataPRO/UnitTest/DatabaseUnitTesting/Utilities/Results/ResultSetParser.cs
- DataPRO/UnitTest/DatabaseUnitTesting/Utilities/Results/XmlFileAdapter.cs
generated_at: "2026-04-16T03:52:19.764167+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "fcd7fb2a1f464396"
---
# Results
## Documentation: Database Unit Testing Result Model
### 1. Purpose
This module provides an in-memory representation and serialization infrastructure for database query result sets, specifically designed for comparison-based unit testing of database operations. It enables capturing the structure (schema) and content (rows) of result sets from `SqlCommand` executions, storing them in structured objects (`Database`, `Table`, `Row`, `Column`), and persisting/loading them to/from XML for deterministic test assertions. The module supports equality comparison of entire result sets (including row multiplicities) and is used internally by the unit testing framework to validate expected vs actual database outputs.
### 2. Public Interface
*Note: All types are `internal` and not exposed outside the `DatabaseUnitTesting.Utilities.Results` namespace.*
- **`Row(string type)`**
Constructor. Initializes a row with a given type (e.g., `"datarow"`, `"schema"`), normalized to lowercase.
- `void AddColumn(Column column)`
Appends a column to the row; invalidates cached `KeyString`.
- `string KeyString { get; }`
Returns a canonical string representation of the row: `type + DELIMITER + col1.SortString + DELIMITER + col2.SortString + ...`. Computed lazily and cached.
- `int ColumnCount { get; }`
Returns the number of columns in the row.
- **`Column(string name, object value)`**
Constructor. Stores column name (unchanged) and value (converted via `Convert`). Precomputes and stores `SortString`.
- `static string Convert(object value)`
Converts values to stable string representations:
- `byte[]``"0x"` + hex digits (e.g., `"0x1A2B"`).
- `DateTime``"M/d/yyyy h:mm:ss tt"` (culture-invariant format), with trailing zeros/colons stripped.
- Other → `value.ToString()`.
- `string Name { get; }`
- `string Value { get; }`
- `string SortString { get; }`
Format: `name.ToLower() + DELIMITER + value`, where `DELIMITER = "\x1f;;"`.
- **`Table(string name1)` / `Table(string name1, string name2)`**
Constructor. Represents a result set table; stores `name1`/`name2` (lowercase).
- `Row Schema { get; set; }`
Schema row defining column names and types. Setting it updates `_fieldCount`.
- `void AddField(string name, string type)`
Adds a column to the schema row (using `type.ToLower()` as value). Increments `_fieldCount`.
- `void AddRow(Row row)`
Adds a data row; uses `row.KeyString` as dictionary key (counts multiplicities). Updates `_rowCount` and `_hashCode`.
- `int RowCount { get; }`
- `int FieldCount { get; }`
- `string Name1 { get; }`, `string Name2 { get; }`
- `bool LookupRow(string key, out int count)`
Tries to retrieve row count for a given `KeyString`.
- `IEnumerable<KeyValuePair<string, int>> Rows { get; }`
Enumerates all rows (as `KeyString` → count pairs).
- `override bool Equals(object other)` / `override int GetHashCode()`
Equality based on:
- `RowCount`, `FieldCount`, `Schema.KeyString`
- Matching row keys and counts (via `LookupRow`).
Hash code is sum of row `KeyString.GetHashCode()`.
- **`Database`**
Container for one or more `Table` instances.
- `int TableCount { get; }`
Total number of tables added (including duplicates).
- `IEnumerable<KeyValuePair<Table, int>> Tables { get; }`
Enumerates unique tables and their occurrence counts.
- `void AddTable(Table table)`
Adds a table; increments its count in internal dictionary and `_tableCount`. Updates `_hashCode`.
- `bool ContainsTable(Table table)`
- `int GetCount(Table table)`
- `override bool Equals(object other)` / `override int GetHashCode()`
Equality based on:
- `TableCount`, `GetHashCode()` (sum of table hash codes)
- Matching tables and counts for all entries.
- **`ResultSetParser` (static)**
Converts ADO.NET `SqlCommand` results into `Database` objects.
- `static Database Parse(SqlCommand command)`
Executes command, parses all result sets (via `NextResult()`), and returns a `Database`. Each result set becomes a `Table` named `"Result Set"`.
- `static List<string> SetFields(Table table, DataTable schema)`
Reads schema metadata (`GetSchemaTable()`) to populate `table.Schema`. Builds type strings (e.g., `"varchar(50)"`, `"decimal(10,2)"`). Returns list of column names.
- **`XmlFileAdapter` (static)**
Serializes/deserializes `Database` to/from XML.
- `static Database Read(string filename)`
Loads XML, parses `<object>` elements into `Table`s.
- `static void Write(string filename, Database diffs)`
Writes `Database` to XML with structure:
```xml
<results>
<object name1="..." name2="...">
<row type="schema">...</row>
<row type="datarow">...</row>
...
</object>
...
</results>
```
- `static List<Table> ReadTables(XmlNode xmlRoot)`
Parses `<object>` nodes into `Table`s.
- `static List<Row> ReadRows(XmlNode xmlTable)`
Parses `<row>` children of a table.
- `static void ReadColumns(XmlNode xmlRow, Row row)`
Parses `<column>` children (requires `name`/`value` attributes).
- `static void WriteTable(XmlTextWriter writer, Table table)` / `WriteRow` / `WriteColumn`
Helper methods for XML output.
- **Constants**: `LEFT = "\0L;;"`, `RIGHT = "\0R;;"` used to escape `<`/`>` in column values.
### 3. Invariants
- **Row KeyString**: Always starts with the row type, followed by `DELIMITER` (`"\x1e;;"`), then each columns `SortString` (itself containing `DELIMITER = "\x1f;;"`).
- **Column SortString**: Always `name.ToLower() + "\x1f;;" + value`, where `value` is the result of `Convert(value)`.
- **Table Schema**: Must be set before `FieldCount` is used (set via `Schema` property or `AddField`).
- **Table Equality**: Requires identical row counts, field counts, schema key strings, and identical row key counts.
- **Database Equality**: Requires identical table counts, hash codes, and per-table multiplicities.
- **XML Escaping**: `<` and `>` in column values are escaped as `"\0L;;"` and `"\0R;;"` during XML write, and unescaped during read.
- **Case Sensitivity**: Table/row/column names and types are normalized to lowercase in storage (except `Column.Name` remains original case).
### 4. Dependencies
- **Depends on**:
- `System.Data` (`SqlCommand`, `SqlDataReader`, `DataTable`, `DataRow`)
- `System.Xml` (`XmlDocument`, `XmlTextWriter`, `XmlAttribute`)
- `System.Text` (`StringBuilder`, `Encoding`)
- `System.Security` (unused in current code; possibly legacy)
- **Used by**:
- Unit test infrastructure (e.g., test runners comparing expected/actual `Database` objects via `XmlFileAdapter` or in-memory `Database` comparison).
- No other modules in this codebase are visible; usage is internal to `DatabaseUnitTesting`.
### 5. Gotchas
- **`Table.AddField` does not update `_hashCode`**: The commented-out line `// _hashCode = _hashCode + c.SortString.GetHashCode();` indicates hash code for schema changes is not tracked, violating the `GetHashCode()` contract if schema is modified after initial use. This may cause incorrect equality behavior if `Schema` is reassigned or `AddField` is called after `GetHashCode()` is used (e.g., in `Database` operations).
- **`Column.Name` is case-preserved but `SortString` uses lowercase**: `Name` retains original casing, but `SortString` uses `name.ToLower()`, which may cause unexpected ordering if case sensitivity matters.
- **`Row.KeyString` includes row type**: Two rows with identical columns but different types (e.g., `"schema"` vs `"datarow"`) will have different keys.
- **`Database` counts duplicates**: `AddTable` increments a counter for identical tables (via `Dictionary<Table, int>`), so `TableCount` may exceed unique table count.
- **XML format is rigid**: `ReadColumns` only accepts `<column>` children; any other element throws `XmlSyntaxException`.
- **`Convert(DateTime)` strips trailing zeros**: May cause loss of precision (e.g., `"12:00:00.000"` → `"12:00"`), potentially breaking equality for timestamps with sub-second precision.
- **No validation of column name/type uniqueness**: `AddField` allows duplicate column names; `Row.AddColumn` allows duplicate columns. This may lead to ambiguous schema representations.
- **`Table` equality ignores `Name1`/`Name2`**: Two tables with identical rows but different names (`name1`/`name2`) are considered equal. This may be intentional for result comparison but is non-intuitive.
- **`ResultSetParser` ignores `DBNull` values**: Columns with `DBNull` are omitted from `Row`, reducing column count and altering `KeyString`. This may cause rows with different `DBNull` patterns to be treated as identical.