Skip to content

Commit 3d64464

Browse files
donislawdevclaude
andcommitted
preset: expand a test question into an ordinary recipe
The first preset, and the shape every later one follows. A preset is a function from parameters to a recipe and nothing else. What it returns is recipe SOURCE rather than a structure, and that is the decision the rest of this rests on: PR5 says there are no closed presets, so eject has to give back something editable, and the strongest way to keep that promise is for eject and a run to be handed the same bytes. They cannot drift apart when there is only one of them, and the same parser reads them that reads a file somebody wrote by hand. size-boundaries answers "is a size limit enforced exactly where it is declared". It expands into seven targets - three below the limit, one on it, three above - each with its own size, its own name and its own expectation. The flip from accept to reject sits exactly on the limit, which is the one statement the whole preset exists to make. Not built on the boundary key, and that is worth saying because it looks like it should be. A boundary target makes three files under one id and they share one expectation, while these need three different ones - and the spread reaches seven sizes rather than three. Explicit targets carry both. It also means expected per target was enough, and nothing about the contract had to move. The distances cancel in pairs, so a set comes to exactly seven times the limit. The format document said about twice, which is the number somebody would have planned disk space around. Parameters reuse the format package's Property rather than declaring a second vocabulary for a name, a kind, a default and a sentence. One implementation decides what values are allowed and one wording refuses the rest, so a window drawing a preset field will work like one drawing a format field. --format is read rather than redeclared. CLI.md calls a preset parameter clashing with an existing flag a mistake in the preset, and --format already names the format of a file - the same thing this preset wanted it to mean. So the preset supplies a value through the precedence chain, and Reads is what lets "preset show" still list it, because a setting nobody knows they can change is a setting that is not there. A limit too small for the files below it is refused as a whole set rather than delivered as the part that fits. PR7, and the part that fits is never the part the run was about - the interesting files are the ones nearest the limit. No lock in the registry, deliberately. The format registry beside it carries the single lock in this tree and a guard names the files allowed to be concurrent, so widening that surface is a decision. There is nothing to decide here: registration happens in init and everything reads after. Three guards, three mutations. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 5b0b049 commit 3d64464

4 files changed

Lines changed: 606 additions & 6 deletions

File tree

‎internal/guard/preset_test.go‎

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package guard
2+
3+
import (
4+
"strings"
5+
"testing"
6+
7+
_ "github.com/donislawdev/TestingFilesGenerator/internal/format/all"
8+
"github.com/donislawdev/TestingFilesGenerator/internal/preset"
9+
"github.com/donislawdev/TestingFilesGenerator/internal/recipe"
10+
)
11+
12+
// A preset is a function from parameters to a recipe, and the recipe it
13+
// returns is source rather than a structure - so that what "preset eject"
14+
// prints and what a run consumes are the same bytes. PR5 says there are no
15+
// closed presets, and one set of bytes is the strongest way to keep that true.
16+
//
17+
// Which makes the first thing to guard obvious: the source a preset produces
18+
// has to be a recipe this build accepts. A preset that expands into something
19+
// the parser refuses is a preset nobody can run and nobody can eject.
20+
21+
func TestEveryPresetExpandsIntoARecipeThisBuildAccepts(t *testing.T) {
22+
presets := preset.All()
23+
if len(presets) == 0 {
24+
t.Fatal("no preset is registered - this guard would pass without checking anything")
25+
}
26+
27+
for _, p := range presets {
28+
t.Run(p.ID, func(t *testing.T) {
29+
args, err := p.Settle(nil)
30+
if err != nil {
31+
t.Fatalf("settling the declared defaults failed: %v", err)
32+
}
33+
src, err := p.Expand(args)
34+
if err != nil {
35+
t.Fatalf("expanding with nothing but its own defaults failed: %v", err)
36+
}
37+
rec, err := recipe.Parse(src, p.ID)
38+
if err != nil {
39+
t.Fatalf("the recipe it produced does not parse:\n%v\n--- source ---\n%s", err, src)
40+
}
41+
if len(rec.Targets) == 0 {
42+
t.Error("it expands into a recipe with no targets")
43+
}
44+
for _, target := range rec.Targets {
45+
if target.Group == "" {
46+
t.Errorf("target %q carries no group, so nothing can assert about the class it belongs to", target.ID)
47+
}
48+
}
49+
})
50+
}
51+
}
52+
53+
// The set itself: seven files, three below the limit, one on it, three above,
54+
// and the expectation flipping exactly at the limit. That flip is the whole
55+
// point of the preset - it is what turns a folder of files into a statement
56+
// about somebody's system.
57+
func TestSizeBoundariesPutsTheFlipExactlyAtTheLimit(t *testing.T) {
58+
p, err := preset.Get("size-boundaries")
59+
if err != nil {
60+
t.Fatalf("size-boundaries is not registered: %v", err)
61+
}
62+
args, err := p.Settle(preset.Args{"limit": "10mb", "format": "txt"})
63+
if err != nil {
64+
t.Fatal(err)
65+
}
66+
src, err := p.Expand(args)
67+
if err != nil {
68+
t.Fatal(err)
69+
}
70+
rec, err := recipe.Parse(src, "size-boundaries")
71+
if err != nil {
72+
t.Fatalf("%v\n--- source ---\n%s", err, src)
73+
}
74+
75+
const limit = 10 * 1024 * 1024
76+
if len(rec.Targets) != 7 {
77+
t.Fatalf("expected 7 targets and got %d", len(rec.Targets))
78+
}
79+
80+
var total int64
81+
for _, target := range rec.Targets {
82+
if len(target.Sizes) != 1 {
83+
t.Errorf("%s asks for %d files and every step of the set is one", target.ID, len(target.Sizes))
84+
continue
85+
}
86+
size := target.Sizes[0]
87+
total += size
88+
89+
wantAccept := size <= limit
90+
got := target.Expected == "accept"
91+
if got != wantAccept {
92+
t.Errorf("%s is %d B against a limit of %d and it expects %q",
93+
target.ID, size, limit, target.Expected)
94+
}
95+
if !wantAccept && target.ExpectedReason != "size_limit" {
96+
t.Errorf("%s expects a rejection for the reason %q and the reason is the limit",
97+
target.ID, target.ExpectedReason)
98+
}
99+
}
100+
101+
// The distances cancel in pairs, so the whole set comes to exactly seven
102+
// times the limit. Worth pinning: the format document said about twice,
103+
// which is the number somebody would plan disk space around.
104+
if want := int64(7 * limit); total != want {
105+
t.Errorf("the set comes to %d B and seven times the limit is %d B", total, want)
106+
}
107+
}
108+
109+
// PR7 and the untouchable rule about silence. A limit so small that the files
110+
// below it cannot exist has to be refused as a set, not delivered as the part
111+
// that happened to fit - and the part that fits is never the part the run was
112+
// about, because the interesting files are the ones nearest the limit.
113+
func TestSizeBoundariesRefusesASetItCannotCompleteRatherThanPartOfIt(t *testing.T) {
114+
p, err := preset.Get("size-boundaries")
115+
if err != nil {
116+
t.Fatal(err)
117+
}
118+
// PDF cannot be small, and a limit of 1 kB puts every step below its floor.
119+
args, err := p.Settle(preset.Args{"limit": "1kb", "format": "pdf"})
120+
if err != nil {
121+
t.Fatal(err)
122+
}
123+
124+
src, err := p.Expand(args)
125+
if err == nil {
126+
t.Fatalf("it produced a set for a limit no file of it can reach:\n%s", src)
127+
}
128+
129+
var impossible *preset.ImpossibleError
130+
if !asImpossible(err, &impossible) {
131+
t.Fatalf("the refusal is %T and it should say the set cannot be built: %v", err, err)
132+
}
133+
for _, want := range []string{"size-boundaries", "--limit"} {
134+
if !strings.Contains(err.Error(), want) {
135+
t.Errorf("the refusal does not mention %q:\n%s", want, err)
136+
}
137+
}
138+
}
139+
140+
func asImpossible(err error, target **preset.ImpossibleError) bool {
141+
if e, ok := err.(*preset.ImpossibleError); ok {
142+
*target = e
143+
return true
144+
}
145+
return false
146+
}

‎internal/preset/doc.go‎

Lines changed: 0 additions & 6 deletions
This file was deleted.

0 commit comments

Comments
 (0)