Files
2026-04-17 14:55:32 -04:00

9.3 KiB
Raw Permalink Blame History

source_files, generated_at, model, schema_version, sha256
source_files generated_at model schema_version sha256
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
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 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 Tables.
    • static void Write(string filename, Database diffs)
      Writes Database to 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 into Tables.
    • 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.