Skip to content

Refine DBF loader tests to generate fixtures at runtime#2

Merged
luckydizzier merged 1 commit intomainfrom
codex/2025-10-01_00-42-47_add-dbftableloader-service-and-tests
Oct 1, 2025
Merged

Refine DBF loader tests to generate fixtures at runtime#2
luckydizzier merged 1 commit intomainfrom
codex/2025-10-01_00-42-47_add-dbftableloader-service-and-tests

Conversation

@luckydizzier
Copy link
Copy Markdown
Owner

@luckydizzier luckydizzier commented Oct 1, 2025

Summary

  • replace static DBF fixture files with runtime-generated test tables and sidecars
  • update DBF loader and catalog tests to use temporary workspaces instead of copied assets
  • clean the test project file by removing fixture copy directives

Testing

  • dotnet test xBase.sln --configuration Release

https://chatgpt.com/codex/tasks/task_e_68dc735dfe7483229e0993b3b52d15df

Summary by CodeRabbit

  • New Features

    • Added a dbfinfo command to display DBF table metadata for files or directories, including fields, record counts, versions, and detected memo/index sidecars. Results are listed in a case-insensitive sorted order.
  • Documentation

    • Updated guidance on DBF table discovery and how metadata is exposed across tools.
  • Tests

    • Added tests covering header parsing, sidecar detection, and directory enumeration with sorted output.

Copilot AI review requested due to automatic review settings October 1, 2025 00:42
@luckydizzier luckydizzier merged commit 55e0c92 into main Oct 1, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Oct 1, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Adds DBF metadata types, a DBF table loader, and a table catalog for directory enumeration. Updates tooling to include a dbfinfo command to display metadata. Documents metadata surfacing in architecture.md. Introduces tests generating synthetic DBF files to validate header parsing, sidecar detection, and catalog ordering. Minor .csproj formatting.

Changes

Cohort / File(s) Summary
Docs
architecture.md
Documents DbfTableLoader surfacing DbfTableDescriptor and TableCatalog discovering .dbf files and sidecars.
Core metadata types
src/XBase.Core/Table/DbfMetadata.cs
Adds DbfFieldSchema, DbfSidecarManifest, and DbfTableDescriptor implementing ITableDescriptor; maps field schemas to descriptors; captures sidecar memo/index info.
DBF loader
src/XBase.Core/Table/DbfTableLoader.cs
Introduces DbfTableLoader with Load(string): validates path, parses DBF header and field descriptors, detects sidecars (memo/index), returns DbfTableDescriptor.
Table catalog
src/XBase.Core/Table/TableCatalog.cs
Adds TableCatalog to enumerate .dbf files in a directory, load via DbfTableLoader, and return case-insensitively sorted descriptors.
Tools CLI
src/XBase.Tools/Program.cs
Refactors command dispatch to queue-based handlers; adds dbfinfo command to print DBF/table metadata; updates usage and error handling.
Tests
tests/XBase.Core.Tests/DbfTableLoaderTests.cs, tests/XBase.Core.Tests/XBase.Core.Tests.csproj
Adds loader and catalog tests with a workspace to synthesize DBF files and sidecars; verifies metadata parsing and enumeration; .csproj formatting only.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Caller as Caller
  participant Loader as DbfTableLoader
  participant FS as File System

  Caller->>Loader: Load(filePath)
  Loader->>FS: Open file (read-only)
  Loader->>FS: Read 32-byte header
  Loader->>Loader: Parse version/date/count/lengths/LDID
  alt header has tail
    Loader->>FS: Read remaining header
  end
  loop Field descriptors (32 bytes)
    Loader->>Loader: ReadFieldDescriptor(...)
  end
  Loader->>FS: Probe sidecars (DBT/FPT/NDX/NTX/MDX)
  Loader-->>Caller: DbfTableDescriptor
Loading
sequenceDiagram
  autonumber
  actor User as User
  participant CLI as Tools Program (dbfinfo)
  participant Catalog as TableCatalog
  participant Loader as DbfTableLoader
  participant FS as File System

  User->>CLI: dbfinfo <path>
  alt path is directory
    CLI->>Catalog: EnumerateTables(directory)
    Catalog->>FS: Enumerate *.dbf
    loop each .dbf
      Catalog->>Loader: Load(dbfPath)
      Loader-->>Catalog: DbfTableDescriptor
    end
    Catalog-->>CLI: List<DbfTableDescriptor> (sorted)
  else path is .dbf file
    CLI->>Loader: Load(filePath)
    Loader-->>CLI: DbfTableDescriptor
  end
  CLI->>User: Print table metadata and fields
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

I hop through fields of DBF delight,
New loader lanterns parse the night.
Sidecars parked, indexes neat,
A catalog trail for tidy feet.
With dbfinfo, I thump with glee—
Metadata carrots, all for me! 🥕🐇

✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/2025-10-01_00-42-47_add-dbftableloader-service-and-tests

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1cf3ca2 and cce31cd.

📒 Files selected for processing (7)
  • architecture.md (1 hunks)
  • src/XBase.Core/Table/DbfMetadata.cs (1 hunks)
  • src/XBase.Core/Table/DbfTableLoader.cs (1 hunks)
  • src/XBase.Core/Table/TableCatalog.cs (1 hunks)
  • src/XBase.Tools/Program.cs (4 hunks)
  • tests/XBase.Core.Tests/DbfTableLoaderTests.cs (1 hunks)
  • tests/XBase.Core.Tests/XBase.Core.Tests.csproj (1 hunks)

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR refactors DBF loader tests to generate fixture data at runtime instead of relying on static fixture files. It replaces static DBF fixture files with runtime-generated test tables and sidecars, updates tests to use temporary workspaces, and adds a new dbfinfo command-line tool.

  • Introduces runtime DBF fixture generation through DbfFixtureWorkspace and helper classes
  • Adds new TableCatalog class for discovering and enumerating DBF files in directories
  • Extends the command-line tool with a dbfinfo command for inspecting DBF metadata

Reviewed Changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/XBase.Core.Tests/DbfTableLoaderTests.cs Complete rewrite using runtime fixture generation instead of static files
src/XBase.Tools/Program.cs Added dbfinfo command with argument queue processing for DBF inspection
src/XBase.Core/Table/TableCatalog.cs New class for discovering and enumerating DBF tables in directories
src/XBase.Core/Table/DbfTableLoader.cs New DBF file loader with header parsing and sidecar detection
src/XBase.Core/Table/DbfMetadata.cs New metadata classes for DBF table descriptors and field schemas
architecture.md Updated documentation to reflect new loader and catalog components

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

public string CreateSidecar(string fileName)
{
string path = Path.Combine(DirectoryPath, fileName);
File.WriteAllBytes(path, Array.Empty<byte>());
Copy link

Copilot AI Oct 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using Array.Empty<byte>() for creating empty files is unnecessary. Use File.Create(path).Dispose() or using var _ = File.Create(path); instead to avoid allocating an empty byte array.

Suggested change
File.WriteAllBytes(path, Array.Empty<byte>());
File.Create(path).Dispose();

Copilot uses AI. Check for mistakes.
Comment on lines +185 to +186
byte[] buffer = new byte[dataLength];
stream.Write(buffer);
Copy link

Copilot AI Oct 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For large record counts, this could allocate significant memory. Consider using stream.SetLength() or writing in smaller chunks to avoid large memory allocations for test fixtures.

Suggested change
byte[] buffer = new byte[dataLength];
stream.Write(buffer);
// Write in chunks to avoid large allocations
const int chunkSize = 4096;
byte[] buffer = new byte[Math.Min(chunkSize, dataLength)];
int bytesRemaining = dataLength;
while (bytesRemaining > 0)
{
int bytesToWrite = Math.Min(buffer.Length, bytesRemaining);
stream.Write(buffer, 0, bytesToWrite);
bytesRemaining -= bytesToWrite;
}

Copilot uses AI. Check for mistakes.
foreach (DbfTableDescriptor table in tables)
{
fileLookup.TryGetValue(table.Name, out string? actualPath);
PrintTable(table, actualPath ?? Path.Combine(target, table.Name + ".dbf"));
Copy link

Copilot AI Oct 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fallback path construction using Path.Combine(target, table.Name + \".dbf\") may not match the actual file if casing differs. Consider using the file enumeration result directly or handling case sensitivity consistently.

Suggested change
PrintTable(table, actualPath ?? Path.Combine(target, table.Name + ".dbf"));
if (actualPath != null)
{
PrintTable(table, actualPath);
}
else
{
Console.Error.WriteLine($"Warning: File for table '{table.Name}' not found in directory '{target}'.");
PrintTable(table, null);
}

Copilot uses AI. Check for mistakes.
Comment on lines +132 to +137
foreach (string extension in IndexExtensions)
{
string candidate = tableName + extension;
if (lookup.TryGetValue(candidate, out string? actualName) && actualName is not null)
{
if (!indexFileNames.Contains(actualName, StringComparer.OrdinalIgnoreCase))
Copy link

Copilot AI Oct 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using Contains() with StringComparer.OrdinalIgnoreCase on a List has O(n) complexity. Consider using a HashSet<string> with the comparer for O(1) lookups when checking for duplicates.

Suggested change
foreach (string extension in IndexExtensions)
{
string candidate = tableName + extension;
if (lookup.TryGetValue(candidate, out string? actualName) && actualName is not null)
{
if (!indexFileNames.Contains(actualName, StringComparer.OrdinalIgnoreCase))
HashSet<string> indexFileNamesSet = new(StringComparer.OrdinalIgnoreCase);
foreach (string extension in IndexExtensions)
{
string candidate = tableName + extension;
if (lookup.TryGetValue(candidate, out string? actualName) && actualName is not null)
{
if (indexFileNamesSet.Add(actualName))

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants