Implement catalog idempotent publish skips

This commit is contained in:
2026-06-19 16:57:38 +00:00
parent 484fda2514
commit 2e0d903626
7 changed files with 365 additions and 6 deletions

View File

@@ -781,6 +781,13 @@ func TestRunNotifiesAfterReplacement(t *testing.T) {
if err := Run(context.Background(), RunOptions{ConfigPath: configPath}); err != nil {
t.Fatalf("first Run() error = %v", err)
}
writeSourceBundle(t, sourceRoot, "", testBundleOptions{
Created: testutil.DefaultCreated.Add(time.Hour),
Files: []testFile{
{Path: "report.md", Data: "# Report\nNew.\n"},
{Path: "summary.txt", Data: "New summary\n"},
},
})
notifier := &recordingNotifier{}
err := Run(context.Background(), RunOptions{
@@ -1063,6 +1070,13 @@ func TestRunNotifiesForAdditiveUpsert(t *testing.T) {
if err := Run(context.Background(), RunOptions{ConfigPath: configPath}); err != nil {
t.Fatalf("first Run() error = %v", err)
}
writeSourceBundle(t, sourceRoot, "", testBundleOptions{
Created: testutil.DefaultCreated.Add(time.Hour),
Files: []testFile{
{Path: "report.md", Data: "# Report\nNew.\n"},
{Path: "summary.txt", Data: "New summary\n"},
},
})
notifier := &recordingNotifier{}
err := Run(context.Background(), RunOptions{ConfigPath: configPath, Notifier: notifier})
@@ -1366,7 +1380,6 @@ func TestRunReplacesHTMLIndexOutput(t *testing.T) {
}
func TestRunSkipsWhenDestinationStateMatches(t *testing.T) {
t.Skip("idempotent write is allowed for matching catalog outputs")
sourceRoot := t.TempDir()
destinationRoot := t.TempDir()
writeSourceBundle(t, sourceRoot, "", testBundleOptions{})
@@ -1374,6 +1387,55 @@ func TestRunSkipsWhenDestinationStateMatches(t *testing.T) {
if err := Run(context.Background(), RunOptions{ConfigPath: configPath}); err != nil {
t.Fatalf("first Run() error = %v", err)
}
statePath := filepath.Join(destinationRoot, storage.StateFileName)
stateBefore, err := os.ReadFile(statePath)
if err != nil {
t.Fatalf("read state before second run: %v", err)
}
reportBefore, err := os.ReadFile(filepath.Join(destinationRoot, "report.md"))
if err != nil {
t.Fatalf("read report before second run: %v", err)
}
notifier := &recordingNotifier{}
var stdout bytes.Buffer
err = Run(context.Background(), RunOptions{ConfigPath: configPath, Stdout: &stdout, Notifier: notifier})
if err != nil {
t.Fatalf("second Run() error = %v", err)
}
if !strings.Contains(stdout.String(), "action=skip_same") {
t.Fatalf("stdout = %q, want skip_same", stdout.String())
}
if !strings.Contains(stdout.String(), "Final status: ok planned=1 publish_new=0 upsert_additive=0 replace_catalog=0 skip_same=1") {
t.Fatalf("stdout = %q, want skip_same summary", stdout.String())
}
stateAfter, err := os.ReadFile(statePath)
if err != nil {
t.Fatalf("read state after second run: %v", err)
}
if string(stateAfter) != string(stateBefore) {
t.Fatalf("state changed during skip")
}
reportAfter, err := os.ReadFile(filepath.Join(destinationRoot, "report.md"))
if err != nil {
t.Fatalf("read report after second run: %v", err)
}
if string(reportAfter) != string(reportBefore) {
t.Fatalf("report changed during skip")
}
if got, want := len(notifier.events), 0; got != want {
t.Fatalf("notification count = %d, want %d", got, want)
}
}
func TestRunReplacementSkipsWhenDestinationStateMatches(t *testing.T) {
sourceRoot := t.TempDir()
destinationRoot := t.TempDir()
writeSourceBundle(t, sourceRoot, "", testBundleOptions{})
configPath := writeLocalConfigWithWorkflow(t, sourceRoot, destinationRoot, config.PathMappingPreserveRelative, config.WorkflowReplacement)
if err := Run(context.Background(), RunOptions{ConfigPath: configPath}); err != nil {
t.Fatalf("first Run() error = %v", err)
}
var stdout bytes.Buffer
err := Run(context.Background(), RunOptions{ConfigPath: configPath, Stdout: &stdout})
@@ -1561,8 +1623,8 @@ func TestRunExercisesRemoteBackendShapesThroughCommonPath(t *testing.T) {
if err := runConfigWithBackendFactory(context.Background(), cfg, RunOptions{Stdout: &repeatOutput}, provider); err != nil {
t.Fatalf("repeat error = %v", err)
}
if got := strings.Count(repeatOutput.String(), "action=upsert_additive"); got != 4 {
t.Fatalf("repeat output = %q, upsert_additive count = %d, want 4", repeatOutput.String(), got)
if got := strings.Count(repeatOutput.String(), "action=skip_same"); got != 4 {
t.Fatalf("repeat output = %q, skip_same count = %d, want 4", repeatOutput.String(), got)
}
}