package appconfig import ( "crypto/sha256" "encoding/hex" "errors" "os" "testing" ) // TestRenderHeldBeforeAnswersTheHashBeforeTheWrite (moves Plan 5 Task 9a, // fix round 1, I1): the caller learns the sha256 of the exact bytes the // render is about to place, before anything is written, so it can journal // its ownership first; the write that follows places those bytes; a refusal // from the callback writes nothing and sends no ack. func TestRenderHeldBeforeAnswersTheHashBeforeTheWrite(t *testing.T) { ctx, s, reg, dd, privB64, _ := execKit(t) priv, err := ParsePrivateKey(privB64, "test") if err != nil { t.Fatal(err) } heldFixture(t, ctx, s) rt := landingRow() if err := reg.Upsert(rt); err != nil { t.Fatal(err) } path := ConfigEnvPath(dd, rt) refused := errors.New("the journal refused") if _, err := RenderHeldBefore(ctx, s, priv, dd, rt, heldOp, heldBox, func(string) error { return refused }); !errors.Is(err, refused) { t.Fatalf("a refusing callback: %v", err) } if _, err := os.Stat(path); !os.IsNotExist(err) { t.Fatalf("a refused render wrote the file: %v", err) } if _, _, err := s.Get(ctx, ConfigAckKey(heldBox, heldHost)); err == nil { t.Fatal("a refused render wrote the ack") } var told string got, err := RenderHeldBefore(ctx, s, priv, dd, rt, heldOp, heldBox, func(sha string) error { if _, err := os.Stat(path); !os.IsNotExist(err) { t.Errorf("the file was there before the callback: %v", err) } told = sha return nil }) if err != nil { t.Fatal(err) } body, err := os.ReadFile(path) if err != nil { t.Fatal(err) } sum := sha256.Sum256(body) if want := hex.EncodeToString(sum[:]); told != want || got.SHA256 != want { t.Fatalf("callback %q, render %q, file %q", told, got.SHA256, want) } if a := readAck(t, ctx, s); a.FileSHA256 != told { t.Fatalf("ack %q, callback %q", a.FileSHA256, told) } }