R-105 (decision 169): retire the never-built slim DR record fields (no writer, no reader); 05 §9/§11, 06 §3.5 corrected
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"os"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// R-105 option A (`09` §3 decision 169): the two "slim DR record" fields are retired. Nothing writes
|
||||
// `host_escrow.directive_json` any more (an escrow upload left a hand-made directive overwritten with `{}`), and
|
||||
// nothing reads `hosts.dr_record_json` or the directive. The columns STAY (a column nobody reads is harmless,
|
||||
// and dropping one is a schema change the hub's database backups would have to follow).
|
||||
|
||||
// COMPANION RED-PROOF (observed): on the pre-R-105 code SaveHostDRBundle overwrote the column — this failed with
|
||||
// "the identity-blob save must leave directive_json alone; got {}". Restored.
|
||||
func TestR105_IdentitySaveLeavesDirectiveColumnAlone(t *testing.T) {
|
||||
st := newResetStore(t)
|
||||
if err := st.UpsertHost(&Host{HostID: "h1", CustomerID: "c1", APIKey: "k"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, _, err := st.SaveHostEscrow("h1", []byte("K"), "fp", "zero_knowledge", "2026-10-07T00:00:00Z", "sha"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := st.db.Exec(`UPDATE host_escrow SET directive_json = '{"hand":"made"}' WHERE host_id = 'h1'`); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := st.SaveHostDRBundle("h1", []byte("ID")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var got string
|
||||
if err := st.db.QueryRow(`SELECT directive_json FROM host_escrow WHERE host_id = 'h1'`).Scan(&got); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got != `{"hand":"made"}` {
|
||||
t.Fatalf("the identity-blob save must leave directive_json alone; got %s", got)
|
||||
}
|
||||
b, err := st.GetHostDRBundle("h1")
|
||||
if err != nil || b == nil || string(b.IdentityBlob) != "ID" || string(b.KEscrowBlob) != "K" {
|
||||
t.Fatalf("the bundle must still carry both blobs; got %+v err=%v", b, err)
|
||||
}
|
||||
}
|
||||
|
||||
// No reader: outside the schema statements, the column names do not appear in the store's source, so a new reader
|
||||
// cannot appear without this design being revisited.
|
||||
// COMPANION RED-PROOF (observed): with `DRRecordJSON` still scanned this failed naming `dr_record_json` in the
|
||||
// hosts select list. Restored.
|
||||
func TestR105_RetiredColumnsHaveNoReader(t *testing.T) {
|
||||
src, err := os.ReadFile("store.go")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
schema := regexp.MustCompile(`(?m)^\s*dr_record_json\s+TEXT NOT NULL DEFAULT '\{\}',$|ALTER TABLE host_escrow ADD COLUMN directive_json`)
|
||||
rest := schema.ReplaceAllString(string(src), "")
|
||||
for _, name := range []string{"dr_record_json", "directive_json", "DRRecordJSON", "DirectiveJSON"} {
|
||||
if regexp.MustCompile(`\b` + name + `\b`).MatchString(stripComments(rest)) {
|
||||
t.Errorf("%s is still read or written in store.go outside its schema statement", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func stripComments(s string) string {
|
||||
return regexp.MustCompile(`(?m)//.*$`).ReplaceAllString(s, "")
|
||||
}
|
||||
Reference in New Issue
Block a user