init
This commit is contained in:
@@ -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 column’s `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.
|
||||
Reference in New Issue
Block a user