Skip to content

Commit c496ac6

Browse files
author
mforce
committed
fix(#417): codex round 1 — unconditional build, broad path guard, column-level coverage
- generate.sh builds the Release solution unconditionally: the previous existence check reused a stale assembly after a migration was added and documented the PREVIOUS schema as current. Incremental build is near-free when everything is fresh. - The environment-leak guard's path rule is now any /-leading token (plus case-insensitive drive letters), not a prefix list — the generator's own /work container mount is identical on every machine, so only this guard can catch that class of leak. Verified against the real tbls output: legitimate content has no /-leading tokens. - The completeness guard now sweeps information_schema.columns: every column must appear as a table cell of its own page, so an omitted column can't hide behind a page that merely exists (or the same word in prose). Mutation-verified: an injected /work path fails the leak guard by name, and a deleted column row fails the coverage guard naming the column (first attempt deleted a nonexistent 'Notes' row and proved nothing — the applied mutant is the one that counts).
1 parent 34423ea commit c496ac6

2 files changed

Lines changed: 33 additions & 5 deletions

File tree

tests/Cluckwork.Api.IntegrationTests/SchemaDocsTests.cs

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,16 @@ public void CommittedSchemaDocs_CarryNoEnvironmentSpecificContent()
113113
Assert.True(Directory.Exists(DocsDir),
114114
"docs/schema/ does not exist — run tools/schema-docs/generate.sh");
115115

116+
// The path rule is deliberately BROAD (any /-leading token, not a
117+
// list of known prefixes): the generator's own /work container mount
118+
// is identical on every machine, so a leak of it would survive the
119+
// CI byte-diff — this guard is the only thing that can catch that
120+
// class. Verified against the real tbls output: legitimate content
121+
// contains no /-leading tokens at all.
116122
var leaks = new (string Name, Regex Pattern)[]
117123
{
118-
("absolute unix path", new Regex(@"(?:/home/|/tmp/|/Users/)")),
119-
("absolute windows path", new Regex(@"[A-Z]:\\")),
124+
("absolute unix path", new Regex(@"(?m)(?:^|[\s(""'`=])/[A-Za-z0-9._-]+(?:/[A-Za-z0-9._-]*)*")),
125+
("absolute windows path", new Regex(@"(?i)[a-z]:\\")),
120126
("timestamp", new Regex(@"\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}")),
121127
("connection URI (would carry the ephemeral password)", new Regex(@"postgres(?:ql)?://")),
122128
};
@@ -169,6 +175,26 @@ public async Task CommittedSchemaDocs_CoverEveryTableIndexAndCheckConstraint()
169175
missing.AppendLine($"table without a doc page: {table}");
170176
}
171177

178+
// Column-level completeness: a page existing does not prove it lists
179+
// every column. Each column must appear as a cell of its own table's
180+
// page (the "| Name |" form tbls emits), so an omitted column can't
181+
// hide behind the same word appearing in prose elsewhere.
182+
var pageCache = new Dictionary<string, string>();
183+
foreach (var row in await QueryPairsAsync(conn,
184+
"""
185+
SELECT table_name, column_name FROM information_schema.columns
186+
WHERE table_schema = 'public'
187+
ORDER BY table_name, ordinal_position
188+
"""))
189+
{
190+
var page = Path.Combine(DocsDir, $"public.{row.Name}.md");
191+
if (!File.Exists(page)) continue; // already reported above
192+
if (!pageCache.TryGetValue(page, out var content))
193+
pageCache[page] = content = File.ReadAllText(page);
194+
if (!content.Contains($"| {row.Def} |", StringComparison.Ordinal))
195+
missing.AppendLine($"column absent from its table page: {row.Name}.{row.Def}");
196+
}
197+
172198
foreach (var row in await QueryPairsAsync(conn,
173199
"SELECT indexname, indexdef FROM pg_indexes WHERE schemaname = 'public' ORDER BY indexname"))
174200
{

tools/schema-docs/generate.sh

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,12 @@ done
5757

5858
PORT="$(docker inspect -f '{{ (index (index .NetworkSettings.Ports "5432/tcp") 0).HostPort }}' "$PG")"
5959

60+
# Always build — an existence check would happily reuse a stale assembly
61+
# after a migration was added and document the PREVIOUS schema as current.
62+
# The incremental build is near-free when everything is already fresh (CI's
63+
# Build step just ran; locally MSBuild skips up-to-date projects).
6064
APP_DLL="src/Cluckwork.Api/bin/Release/net10.0/Cluckwork.Api.dll"
61-
if [ ! -f "$APP_DLL" ]; then
62-
dotnet build Cluckwork.sln --configuration Release
63-
fi
65+
dotnet build Cluckwork.sln --configuration Release
6466

6567
# Testing environment — the same one the integration-test factory uses, and
6668
# for the same reason: not Development (which would pull the developer's

0 commit comments

Comments
 (0)