ajhahn.de
← eeco
Go 38 lines
package brief

import (
	"path/filepath"
	"strings"
	"testing"

	"github.com/ajhahnde/eeco/internal/config"
)

// writeMalformedFact drops a .md with broken frontmatter (opening '---', no
// closing '---') into the memory dir so memory.LoadAll's ParseFact step aborts
// the whole load. Named *.md and not dot-prefixed so LoadAll does not skip it.
func writeMalformedFact(t *testing.T, cfg *config.Config) {
	t.Helper()
	writeFile(t, filepath.Join(cfg.Workspace, "memory", "broken.md"), "---\nname: broken\n")
}

// TestBoundary_BriefFailsClosedOnMalformedMemory pins the foreground arm of
// the H1.6 degrade matrix: a deliberately-invoked `eeco go` fails LOUD on a
// corrupt knowledge layer (exit 1) rather than silently distilling a
// misleading brief. Paired with internal/hooks
// TestBoundary_SessionEmitDegradesOpenOnMalformedMemory, which pins the
// background hook's fail-OPEN posture. The asymmetry is intentional (Option A,
// H1.6): each boundary degrades-open cleanly OR fails-loud cleanly, never the
// dangerous middle of a wrong/partial result. No production change.
func TestBoundary_BriefFailsClosedOnMalformedMemory(t *testing.T) {
	cfg := sampleRepo(t)
	writeMalformedFact(t, cfg)

	if _, err := Collect(cfg); err == nil || !strings.Contains(err.Error(), "load memory") {
		t.Fatalf("Collect must fail closed with a load-memory error, got %v", err)
	}
	if _, err := Render(cfg); err == nil {
		t.Fatalf("Render must fan the load-memory failure (eeco go exit 1), got nil")
	}
}