9.3 KiB
source_files, generated_at, model, schema_version, sha256
| source_files | generated_at | model | schema_version | sha256 | ||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
2026-04-16T03:52:19.764167+00:00 | Qwen/Qwen3-Coder-Next-FP8 | 1 | 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 cachedKeyString.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 viaConvert). Precomputes and storesSortString.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, whereDELIMITER = "\x1f;;".
-
Table(string name1)/Table(string name1, string name2)
Constructor. Represents a result set table; storesname1/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 (usingtype.ToLower()as value). Increments_fieldCount.void AddRow(Row row)
Adds a data row; usesrow.KeyStringas dictionary key (counts multiplicities). Updates_rowCountand_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 givenKeyString.IEnumerable<KeyValuePair<string, int>> Rows { get; }
Enumerates all rows (asKeyString→ 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 rowKeyString.GetHashCode().
-
Database
Container for one or moreTableinstances.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.NETSqlCommandresults intoDatabaseobjects.static Database Parse(SqlCommand command)
Executes command, parses all result sets (viaNextResult()), and returns aDatabase. Each result set becomes aTablenamed"Result Set".static List<string> SetFields(Table table, DataTable schema)
Reads schema metadata (GetSchemaTable()) to populatetable.Schema. Builds type strings (e.g.,"varchar(50)","decimal(10,2)"). Returns list of column names.
-
XmlFileAdapter(static)
Serializes/deserializesDatabaseto/from XML.static Database Read(string filename)
Loads XML, parses<object>elements intoTables.static void Write(string filename, Database diffs)
WritesDatabaseto XML with structure:<results> <object name1="..." name2="..."> <row type="schema">...</row> <row type="datarow">...</row> ... </object> ... </results>static List<Table> ReadTables(XmlNode xmlRoot)
Parses<object>nodes intoTables.static List<Row> ReadRows(XmlNode xmlTable)
Parses<row>children of a table.static void ReadColumns(XmlNode xmlRow, Row row)
Parses<column>children (requiresname/valueattributes).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’sSortString(itself containingDELIMITER = "\x1f;;"). - Column SortString: Always
name.ToLower() + "\x1f;;" + value, wherevalueis the result ofConvert(value). - Table Schema: Must be set before
FieldCountis used (set viaSchemaproperty orAddField). - 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.Nameremains 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
Databaseobjects viaXmlFileAdapteror in-memoryDatabasecomparison). - No other modules in this codebase are visible; usage is internal to
DatabaseUnitTesting.
- Unit test infrastructure (e.g., test runners comparing expected/actual
5. Gotchas
Table.AddFielddoes not update_hashCode: The commented-out line// _hashCode = _hashCode + c.SortString.GetHashCode();indicates hash code for schema changes is not tracked, violating theGetHashCode()contract if schema is modified after initial use. This may cause incorrect equality behavior ifSchemais reassigned orAddFieldis called afterGetHashCode()is used (e.g., inDatabaseoperations).Column.Nameis case-preserved butSortStringuses lowercase:Nameretains original casing, butSortStringusesname.ToLower(), which may cause unexpected ordering if case sensitivity matters.Row.KeyStringincludes row type: Two rows with identical columns but different types (e.g.,"schema"vs"datarow") will have different keys.Databasecounts duplicates:AddTableincrements a counter for identical tables (viaDictionary<Table, int>), soTableCountmay exceed unique table count.- XML format is rigid:
ReadColumnsonly accepts<column>children; any other element throwsXmlSyntaxException. 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:
AddFieldallows duplicate column names;Row.AddColumnallows duplicate columns. This may lead to ambiguous schema representations. Tableequality ignoresName1/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.ResultSetParserignoresDBNullvalues: Columns withDBNullare omitted fromRow, reducing column count and alteringKeyString. This may cause rows with differentDBNullpatterns to be treated as identical.