docs(P02): complete Pacts+Partners phase

---ci---
project: oy
phase: 2
milestone: v0.2
status: complete
phase_role: execution
requirements:
  covered: [REQ-020, REQ-018]
  partial: []
---/ci---

Phase 2 (Pacts+Partners) complete. 2 new modules: x/pact (6-PactType enum + Mission Lock,
G-005 one module not six), x/partner (4-tier Partner Spectrum). 207 tests total (143 prev
+ 64 new). Coverage: x/pact 95.9%, x/partner 100%. Lexicon + G-003 invariants green.
Tagged v0.1.2.
This commit is contained in:
2026-08-17 21:23:29 +00:00
parent 93a8a3b311
commit 0fefd88668
7 changed files with 1511 additions and 2 deletions
+80
View File
@@ -0,0 +1,80 @@
package types
import "fmt"
// genesis.go holds the data-engineer's genesis schema helpers for the pact
// module (G-008 split). ValidateGenesis in types.go composes these helpers;
// the security-engineer's test assertions live in types_test.go.
//
// The Pact genesis schema is a single top-level set: Pacts. The two
// invariants enforced at genesis load are (1) pact-id uniqueness (A-212) and
// (2) the Mission-Lock check (the per-type AmendableCoreTerms flags for
// Pause/Ground/Stance must be false — the global MissionLockAmendable const
// bool is the firewall). The Mission-Lock is enforced by compile-time consts;
// the genesis-side MissionLockCheck is the data-engineer's hook that asserts
// the const firewall is intact whenever genesis Pacts are loaded (so a
// future change to the consts would surface here too).
// ValidatePacts asserts pact-ids are present and unique, and that each
// Pact's type is a known PactType. It also runs the Mission-Lock check
// (MissionLockCheck) so the genesis load path enforces both invariants.
// ValidatePacts is the data-engineer's schema validator, composed by
// ValidateGenesis in types.go.
func ValidatePacts(pacts []Pact) error {
seen := make(map[string]bool, len(pacts))
for i, p := range pacts {
if p.PactID == "" {
return fmt.Errorf("pact [%d]: empty pact-id", i)
}
if seen[p.PactID] {
return fmt.Errorf("pact: duplicate pact-id %q", p.PactID)
}
seen[p.PactID] = true
if !knownPactType(p.Type) {
return fmt.Errorf("pact %q: unknown pact type %q", p.PactID, p.Type)
}
}
if err := MissionLockCheck(pacts); err != nil {
return err
}
return nil
}
// knownPactType reports whether t is one of the six vision §16 PactType values.
func knownPactType(t PactType) bool {
for _, kt := range AllPactTypes() {
if t == kt {
return true
}
}
return false
}
// MissionLockCheck asserts the Mission-Lock invariant on a slice of Pacts:
// every Pause/Ground/Stance Pact must have its AmendableCoreTerms flag false.
// Because the flags are compile-time consts (AmendableCoreTermsPause/Ground/
// Stance == false) and the global MissionLockAmendable const is false, this
// check always passes — it exists as the data-engineer's genesis-side
// assertion that the Mission-Lock firewall is intact. If the consts ever
// changed to true, this check would still pass (the consts are the firewall,
// not runtime data); the test in types_test.go is the true regression guard.
// The helper is the genesis hook for v0.3 keeper logic to extend with live
// per-pact Mission-Lock enforcement.
func MissionLockCheck(pacts []Pact) error {
// The global MissionLockAmendable const is the firewall: if it were ever
// flipped to true (which the test suite rejects), the genesis load would
// surface it here. The per-pact loop echoes the invariant for each
// Mission-Locked Pact type so a future per-pact check has a hook point.
if MissionLockAmendable {
return fmt.Errorf("pact: Mission Lock amendable (MissionLockAmendable == true) — firewall breach")
}
for _, p := range pacts {
if !MissionLockAmendableCoreTerms(p.Type) {
// Non-amendable core terms: the const flags already guarantee this;
// the genesis check is the echo. No per-pact runtime data to verify
// in the skeleton — the const is the source of truth.
continue
}
}
return nil
}
+235
View File
@@ -0,0 +1,235 @@
package types
import (
"encoding/json"
"fmt"
)
const (
ModuleName = "pact"
StoreKey = ModuleName
RouterKey = ModuleName
QuerierRoute = ModuleName
// PactTypeCount is the locked count of PactType enum values (vision §16).
// A regression firewall: adding/removing/renaming a Pact type breaks this
// const's test (REQ-020, A-207: ONE module with enum, not six micro-modules).
PactTypeCount = 6
// MissionLockAmendable is the Mission-Lock invariant: the core terms of
// Pause/Ground/Stance Pacts are non-amendable (vision §19). This is a
// locked const bool: it can NEVER be set true. The regression test asserts
// it is false and that the per-type AmendableCoreTerms flags for
// Pause/Ground/Stance are all false.
MissionLockAmendable = false
)
// PactType enumerates the six commitment types (vision §16, REQ-020).
// A-207: all six live in ONE x/pact module with a PactType enum + per-type
// execute-entry stubs (NOT six micro-modules).
type PactType string
const (
PactPause PactType = "Pause" // circuit-breaker commitment (wraps x/still)
PactGround PactType = "Ground" // earth-anchored collateral lock commitment
PactStance PactType = "Stance" // public-position / attestation commitment
PactCover PactType = "Cover" // insurance-like commitment (Cover Pool)
PactStandRegistry PactType = "StandRegistry" // registers a Stand into the canonical registry
PactHubAPI PactType = "HubAPI" // B2B backbone commitment
)
// AllPactTypes returns all six PactType values in vision §16 order.
// Locked-const test asserts exactly 6 entries with these names (REQ-020).
func AllPactTypes() []PactType {
return []PactType{
PactPause,
PactGround,
PactStance,
PactCover,
PactStandRegistry,
PactHubAPI,
}
}
// PactStatus enumerates the lifecycle states of a Pact (REQ-020).
type PactStatus string
const (
StatusProposed PactStatus = "Proposed" // pact created, not yet active
StatusActive PactStatus = "Active" // pact is live and binding
StatusFulfilled PactStatus = "Fulfilled" // pact completed successfully
StatusVoided PactStatus = "Voided" // pact voided (cancelled / breached)
)
// PactStatusCount is the locked count of PactStatus enum values.
const PactStatusCount = 4
// Pact is a commitment of one of six types (vision §16, REQ-020). Each Pact
// has a type, parties (Reach IDs by-ID-string per G-003), opaque terms-bytes,
// a status, and per-type execute-message ref. window-id-ref references
// x/window by ID string (G-003 by-ID-string invariant; P1-01-01 convention).
// stand-id-ref references x/stand by ID string (P1-02-01 convention); only
// StandRegistry Pacts populate it for non-empty, others leave it "".
type Pact struct {
PactID string `json:"pact_id" yaml:"pact_id"`
Type PactType `json:"type" yaml:"type"`
Parties []string `json:"parties" yaml:"parties"`
Terms []byte `json:"terms" yaml:"terms"`
Status PactStatus `json:"status" yaml:"status"`
ExecuteMsgRef string `json:"execute_msg_ref" yaml:"execute_msg_ref"`
WindowIDRef string `json:"window_id_ref" yaml:"window_id_ref"`
StandIDRef string `json:"stand_id_ref" yaml:"stand_id_ref"`
}
// MissionLockCoreTerms flags which Pact types have non-amendable core terms
// under the Mission Lock (vision §19). Pause/Ground/Stance core terms are
// non-amendable; the const flags below are the per-type invariant. The
// module-level MissionLockAmendable const bool is the global firewall.
const (
// AmendableCoreTermsPause is false: Pause Pact core terms are
// non-amendable under the Mission Lock.
AmendableCoreTermsPause = false
// AmendableCoreTermsGround is false: Ground Pact core terms are
// non-amendable under the Mission Lock.
AmendableCoreTermsGround = false
// AmendableCoreTermsStance is false: Stance Pact core terms are
// non-amendable under the Mission Lock.
AmendableCoreTermsStance = false
)
// MissionLockAmendableCoreTerms returns the per-type AmendableCoreTerms flag
// for a PactType. Pause/Ground/Stance return false (non-amendable); Cover,
// StandRegistry, HubAPI return true (amendable per the skeleton — these are
// not Mission-Locked). The Mission-Lock invariant test asserts the three
// core types return false.
func MissionLockAmendableCoreTerms(t PactType) bool {
switch t {
case PactPause:
return AmendableCoreTermsPause
case PactGround:
return AmendableCoreTermsGround
case PactStance:
return AmendableCoreTermsStance
default:
return true
}
}
// ExecutePause is the execute-entry stub for a Pause Pact (circuit-breaker).
// The skeleton returns the proposed status transition; v0.3 wires the live
// keeper that wraps x/still.
func (p *Pact) ExecutePause() error {
if p.Type != PactPause {
return fmt.Errorf("ExecutePause: pact %q is type %q, not Pause", p.PactID, p.Type)
}
if p.Status != StatusProposed {
return fmt.Errorf("ExecutePause: pact %q status %q, not Proposed", p.PactID, p.Status)
}
p.Status = StatusActive
return nil
}
// ExecuteGround is the execute-entry stub for a Ground Pact
// (earth-anchored collateral lock).
func (p *Pact) ExecuteGround() error {
if p.Type != PactGround {
return fmt.Errorf("ExecuteGround: pact %q is type %q, not Ground", p.PactID, p.Type)
}
if p.Status != StatusProposed {
return fmt.Errorf("ExecuteGround: pact %q status %q, not Proposed", p.PactID, p.Status)
}
p.Status = StatusActive
return nil
}
// ExecuteStance is the execute-entry stub for a Stance Pact
// (public-position / attestation).
func (p *Pact) ExecuteStance() error {
if p.Type != PactStance {
return fmt.Errorf("ExecuteStance: pact %q is type %q, not Stance", p.PactID, p.Type)
}
if p.Status != StatusProposed {
return fmt.Errorf("ExecuteStance: pact %q status %q, not Proposed", p.PactID, p.Status)
}
p.Status = StatusActive
return nil
}
// ExecuteCover is the execute-entry stub for a Cover Pact (insurance-like).
// Cover Pool seniority is deferred per Q7 — the skeleton is a flat
// commitment type with no seniority fields.
func (p *Pact) ExecuteCover() error {
if p.Type != PactCover {
return fmt.Errorf("ExecuteCover: pact %q is type %q, not Cover", p.PactID, p.Type)
}
if p.Status != StatusProposed {
return fmt.Errorf("ExecuteCover: pact %q status %q, not Proposed", p.PactID, p.Status)
}
p.Status = StatusActive
return nil
}
// ExecuteStandRegistry is the execute-entry stub for a StandRegistry Pact.
// stand-id-ref references x/stand by ID string (G-003); the skeleton activates
// the pact without a live keeper call.
func (p *Pact) ExecuteStandRegistry() error {
if p.Type != PactStandRegistry {
return fmt.Errorf("ExecuteStandRegistry: pact %q is type %q, not StandRegistry", p.PactID, p.Type)
}
if p.Status != StatusProposed {
return fmt.Errorf("ExecuteStandRegistry: pact %q status %q, not Proposed", p.PactID, p.Status)
}
if p.StandIDRef == "" {
return fmt.Errorf("ExecuteStandRegistry: pact %q missing stand-id-ref", p.PactID)
}
p.Status = StatusActive
return nil
}
// ExecuteHubAPI is the execute-entry stub for a HubAPI Pact (B2B backbone).
// The full Hub API suite is deferred to Phase 3; v0.2 = stub type only.
func (p *Pact) ExecuteHubAPI() error {
if p.Type != PactHubAPI {
return fmt.Errorf("ExecuteHubAPI: pact %q is type %q, not HubAPI", p.PactID, p.Type)
}
if p.Status != StatusProposed {
return fmt.Errorf("ExecuteHubAPI: pact %q status %q, not Proposed", p.PactID, p.Status)
}
p.Status = StatusActive
return nil
}
// Params for the pact module (skeleton — no tunables in v0.2).
type Params struct{}
func DefaultParams() Params { return Params{} }
// GenesisState defines the pact module genesis state (REQ-020).
// Pacts is the top-level set; ValidateGenesis enforces pact-id uniqueness and
// the Mission-Lock check (Mission-Locked types' AmendableCoreTerms flags must
// be false). The data-engineer's genesis.go holds the schema helpers (G-008).
type GenesisState struct {
Params Params `json:"params" yaml:"params"`
Pacts []Pact `json:"pacts" yaml:"pacts"`
}
func DefaultGenesisState() *GenesisState {
return &GenesisState{
Params: DefaultParams(),
Pacts: []Pact{},
}
}
// ValidateGenesis performs ID-uniqueness checks (A-212 upgrade from v0.1
// no-op): rejects duplicate pact-ids, and runs the Mission-Lock check on
// genesis Pacts. Delegates to the data-engineer's genesis.go helpers (G-008).
func ValidateGenesis(bz json.RawMessage) error {
var gs GenesisState
if err := json.Unmarshal(bz, &gs); err != nil {
return fmt.Errorf("pact: invalid genesis: %w", err)
}
if err := ValidatePacts(gs.Pacts); err != nil {
return fmt.Errorf("pact: %w", err)
}
return nil
}
+449
View File
@@ -0,0 +1,449 @@
package types_test
import (
"encoding/json"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
"github.com/oy/openyield/lexicon"
"github.com/oy/openyield/x/pact/types"
)
// TestPactTypeCountLockedConst asserts PactTypeCount is exactly 6 and
// AllPactTypes() returns exactly 6 (vision §16, REQ-020, A-207). A regression
// firewall: adding/removing/renaming a Pact type breaks this test.
func TestPactTypeCountLockedConst(t *testing.T) {
if types.PactTypeCount != 6 {
t.Errorf("PactTypeCount = %d, expected 6 (vision §16 LOCKED)", types.PactTypeCount)
}
all := types.AllPactTypes()
if len(all) != 6 {
t.Errorf("AllPactTypes() len = %d, expected 6", len(all))
}
}
// TestAllPactTypesNames asserts the 6 vision §16 names in order with no
// extras, no dups, no renames.
func TestAllPactTypesNames(t *testing.T) {
want := []string{
"Pause", "Ground", "Stance", "Cover", "StandRegistry", "HubAPI",
}
all := types.AllPactTypes()
if len(all) != len(want) {
t.Fatalf("len = %d, want %d", len(all), len(want))
}
seen := map[string]bool{}
for i, s := range all {
if string(s) != want[i] {
t.Errorf("AllPactTypes()[%d] = %q, want %q", i, s, want[i])
}
if seen[string(s)] {
t.Errorf("duplicate PactType %q", s)
}
seen[string(s)] = true
}
}
// TestPactTypeValues asserts each named const matches its AllPactTypes entry.
func TestPactTypeValues(t *testing.T) {
if types.PactPause != "Pause" {
t.Errorf("PactPause = %q", types.PactPause)
}
if types.PactGround != "Ground" {
t.Errorf("PactGround = %q", types.PactGround)
}
if types.PactStance != "Stance" {
t.Errorf("PactStance = %q", types.PactStance)
}
if types.PactCover != "Cover" {
t.Errorf("PactCover = %q", types.PactCover)
}
if types.PactStandRegistry != "StandRegistry" {
t.Errorf("PactStandRegistry = %q", types.PactStandRegistry)
}
if types.PactHubAPI != "HubAPI" {
t.Errorf("PactHubAPI = %q", types.PactHubAPI)
}
}
// TestPactStatusCountLockedConst asserts PactStatusCount is exactly 4.
func TestPactStatusCountLockedConst(t *testing.T) {
if types.PactStatusCount != 4 {
t.Errorf("PactStatusCount = %d, expected 4", types.PactStatusCount)
}
}
// TestPactStatusEnumCoverage asserts all four PactStatus values are distinct
// and non-empty (REQ-020 lifecycle: Proposed, Active, Fulfilled, Voided).
func TestPactStatusEnumCoverage(t *testing.T) {
statuses := []types.PactStatus{
types.StatusProposed, types.StatusActive,
types.StatusFulfilled, types.StatusVoided,
}
if len(statuses) != 4 {
t.Errorf("expected 4 PactStatus consts, got %d", len(statuses))
}
seen := map[types.PactStatus]bool{}
for _, s := range statuses {
if s == "" {
t.Error("empty PactStatus")
}
if seen[s] {
t.Errorf("duplicate PactStatus %q", s)
}
seen[s] = true
}
}
// TestMissionLockAmendableConstFalse asserts the global Mission-Lock const
// is false (vision §19): the Mission Lock can NEVER be amended. This is the
// highest-severity regression firewall for the pact module.
func TestMissionLockAmendableConstFalse(t *testing.T) {
if types.MissionLockAmendable != false {
t.Fatalf("MissionLockAmendable = %v, expected false (Mission Lock non-amendable)", types.MissionLockAmendable)
}
}
// TestMissionLockCoreTermsNonAmendable asserts the per-type AmendableCoreTerms
// const flags for Pause/Ground/Stance are all false (Mission-Lock invariant).
// Cover/StandRegistry/HubAPI return true (amendable — not Mission-Locked).
func TestMissionLockCoreTermsNonAmendable(t *testing.T) {
// Pause/Ground/Stance core terms MUST be non-amendable.
if types.AmendableCoreTermsPause != false {
t.Error("AmendableCoreTermsPause must be false (Mission Lock)")
}
if types.AmendableCoreTermsGround != false {
t.Error("AmendableCoreTermsGround must be false (Mission Lock)")
}
if types.AmendableCoreTermsStance != false {
t.Error("AmendableCoreTermsStance must be false (Mission Lock)")
}
// The MissionLockAmendableCoreTerms helper echoes the const flags.
locked := []types.PactType{types.PactPause, types.PactGround, types.PactStance}
for _, pt := range locked {
if types.MissionLockAmendableCoreTerms(pt) != false {
t.Errorf("MissionLockAmendableCoreTerms(%q) = true, want false (Mission Lock)", pt)
}
}
// Cover/StandRegistry/HubAPI are amendable (not Mission-Locked).
amendable := []types.PactType{types.PactCover, types.PactStandRegistry, types.PactHubAPI}
for _, pt := range amendable {
if types.MissionLockAmendableCoreTerms(pt) != true {
t.Errorf("MissionLockAmendableCoreTerms(%q) = false, want true (amendable)", pt)
}
}
}
// TestPactStructFields asserts Pact carries all required fields including
// the by-ID-string refs (window-id-ref, stand-id-ref per G-003).
func TestPactStructFields(t *testing.T) {
p := types.Pact{
PactID: "p1",
Type: types.PactPause,
Parties: []string{"reach:a", "reach:b"},
Terms: []byte("terms-bytes"),
Status: types.StatusProposed,
ExecuteMsgRef: "msg:pause:1",
WindowIDRef: "w1",
StandIDRef: "s1",
}
if p.PactID != "p1" || p.Type != types.PactPause || len(p.Parties) != 2 ||
string(p.Terms) != "terms-bytes" || p.Status != types.StatusProposed ||
p.ExecuteMsgRef != "msg:pause:1" || p.WindowIDRef != "w1" || p.StandIDRef != "s1" {
t.Error("Pact fields not set correctly")
}
}
// TestPactStructRefsAreStrings asserts window-id-ref and stand-id-ref are
// string-typed (G-003 by-ID-string invariant; the G-003 import invariant is
// enforced project-wide by P1-01-02's go/parser scan, so this test only
// asserts the field types at the struct level, not cross-module imports).
func TestPactStructRefsAreStrings(t *testing.T) {
// Construct a Pact and confirm the ref fields hold plain strings —
// no struct imports of x/window or x/stand are needed.
p := types.Pact{WindowIDRef: "window-abc", StandIDRef: "stand-xyz"}
if p.WindowIDRef != "window-abc" {
t.Errorf("WindowIDRef = %q", p.WindowIDRef)
}
if p.StandIDRef != "stand-xyz" {
t.Errorf("StandIDRef = %q", p.StandIDRef)
}
}
// TestExecuteStubsCallable asserts each per-type Execute* stub is callable
// and transitions a Proposed Pact to Active (REQ-020).
func TestExecuteStubsCallable(t *testing.T) {
tt := []struct {
name string
pact types.Pact
execFn func(*types.Pact) error
}{
{"Pause", types.Pact{PactID: "p1", Type: types.PactPause, Status: types.StatusProposed}, (*types.Pact).ExecutePause},
{"Ground", types.Pact{PactID: "p2", Type: types.PactGround, Status: types.StatusProposed}, (*types.Pact).ExecuteGround},
{"Stance", types.Pact{PactID: "p3", Type: types.PactStance, Status: types.StatusProposed}, (*types.Pact).ExecuteStance},
{"Cover", types.Pact{PactID: "p4", Type: types.PactCover, Status: types.StatusProposed}, (*types.Pact).ExecuteCover},
{"HubAPI", types.Pact{PactID: "p6", Type: types.PactHubAPI, Status: types.StatusProposed}, (*types.Pact).ExecuteHubAPI},
}
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
p := tc.pact
if err := tc.execFn(&p); err != nil {
t.Fatalf("Execute%s: %v", tc.name, err)
}
if p.Status != types.StatusActive {
t.Errorf("after Execute%s, status = %q, want Active", tc.name, p.Status)
}
})
}
}
// TestExecuteStandRegistryRequiresStandIDRef asserts ExecuteStandRegistry
// requires a non-empty stand-id-ref (the by-ID-string ref to x/stand).
func TestExecuteStandRegistryRequiresStandIDRef(t *testing.T) {
p := types.Pact{PactID: "p5", Type: types.PactStandRegistry, Status: types.StatusProposed, StandIDRef: ""}
if err := p.ExecuteStandRegistry(); err == nil {
t.Error("ExecuteStandRegistry should error on empty stand-id-ref")
}
p.StandIDRef = "s1"
if err := p.ExecuteStandRegistry(); err != nil {
t.Errorf("ExecuteStandRegistry with stand-id-ref: %v", err)
}
if p.Status != types.StatusActive {
t.Errorf("status = %q, want Active", p.Status)
}
}
// TestExecuteStubsRejectWrongType asserts each Execute* stub rejects a Pact
// of the wrong type (type guard).
func TestExecuteStubsRejectWrongType(t *testing.T) {
p := types.Pact{PactID: "p", Type: types.PactCover, Status: types.StatusProposed}
if err := p.ExecutePause(); err == nil {
t.Error("ExecutePause on a Cover pact should error")
}
if err := p.ExecuteGround(); err == nil {
t.Error("ExecuteGround on a Cover pact should error")
}
if err := p.ExecuteStance(); err == nil {
t.Error("ExecuteStance on a Cover pact should error")
}
if err := p.ExecuteStandRegistry(); err == nil {
t.Error("ExecuteStandRegistry on a Cover pact should error")
}
if err := p.ExecuteHubAPI(); err == nil {
t.Error("ExecuteHubAPI on a Cover pact should error")
}
// ExecuteCover should succeed (matches type).
if err := p.ExecuteCover(); err != nil {
t.Errorf("ExecuteCover on a Cover pact: %v", err)
}
}
// TestExecuteStubsRejectNonProposed asserts each Execute* stub rejects a
// Pact not in the Proposed status.
func TestExecuteStubsRejectNonProposed(t *testing.T) {
tt := []struct {
name string
pact types.Pact
execFn func(*types.Pact) error
}{
{"Pause-active", types.Pact{PactID: "p", Type: types.PactPause, Status: types.StatusActive}, (*types.Pact).ExecutePause},
{"Ground-fulfilled", types.Pact{PactID: "p", Type: types.PactGround, Status: types.StatusFulfilled}, (*types.Pact).ExecuteGround},
{"Stance-voided", types.Pact{PactID: "p", Type: types.PactStance, Status: types.StatusVoided}, (*types.Pact).ExecuteStance},
{"Cover-active", types.Pact{PactID: "p", Type: types.PactCover, Status: types.StatusActive}, (*types.Pact).ExecuteCover},
{"HubAPI-voided", types.Pact{PactID: "p", Type: types.PactHubAPI, Status: types.StatusVoided}, (*types.Pact).ExecuteHubAPI},
}
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
p := tc.pact
if err := tc.execFn(&p); err == nil {
t.Errorf("Execute%s on %q-status pact should error", tc.name, p.Status)
}
})
}
}
// TestExecuteStandRegistryNonProposed asserts ExecuteStandRegistry rejects
// a non-Proposed StandRegistry pact even when stand-id-ref is set.
func TestExecuteStandRegistryNonProposed(t *testing.T) {
p := types.Pact{PactID: "p", Type: types.PactStandRegistry, Status: types.StatusActive, StandIDRef: "s1"}
if err := p.ExecuteStandRegistry(); err == nil {
t.Error("ExecuteStandRegistry on Active pact should error")
}
}
// TestDefaultGenesisStateEmpty asserts DefaultGenesisState returns a
// non-nil empty slice for Pacts.
func TestDefaultGenesisStateEmpty(t *testing.T) {
gs := types.DefaultGenesisState()
if gs == nil {
t.Fatal("DefaultGenesisState returned nil")
}
if gs.Pacts == nil || len(gs.Pacts) != 0 {
t.Errorf("Default Pacts should be non-nil empty slice; got len=%d nil=%v", len(gs.Pacts), gs.Pacts == nil)
}
}
// TestValidateGenesisRejectsDupPactIDs asserts A-212: duplicate pact-ids
// are rejected (upgrade from v0.1's no-op ValidateGenesis).
func TestValidateGenesisRejectsDupPactIDs(t *testing.T) {
gs := types.GenesisState{
Pacts: []types.Pact{
{PactID: "p1", Type: types.PactPause},
{PactID: "p1", Type: types.PactGround}, // dup
},
}
bz, _ := json.Marshal(gs)
if err := types.ValidateGenesis(bz); err == nil {
t.Error("ValidateGenesis should reject duplicate pact-ids")
}
}
// TestValidateGenesisRejectsEmptyPactID asserts empty pact-id is rejected.
func TestValidateGenesisRejectsEmptyPactID(t *testing.T) {
gs := types.GenesisState{
Pacts: []types.Pact{{PactID: "", Type: types.PactPause}},
}
bz, _ := json.Marshal(gs)
if err := types.ValidateGenesis(bz); err == nil {
t.Error("ValidateGenesis should reject empty pact-id")
}
}
// TestValidateGenesisRejectsUnknownType asserts an unknown PactType is
// rejected (data-engineer schema validation).
func TestValidateGenesisRejectsUnknownType(t *testing.T) {
gs := types.GenesisState{
Pacts: []types.Pact{{PactID: "p1", Type: types.PactType("Bogus")}},
}
bz, _ := json.Marshal(gs)
if err := types.ValidateGenesis(bz); err == nil {
t.Error("ValidateGenesis should reject unknown pact type")
}
}
// TestValidateGenesisRejectsBadJSON asserts malformed JSON is rejected.
func TestValidateGenesisRejectsBadJSON(t *testing.T) {
if err := types.ValidateGenesis(json.RawMessage(`{not json`)); err == nil {
t.Error("ValidateGenesis should reject malformed JSON")
}
}
// TestValidateGenesisAcceptsClean asserts a clean genesis validates.
func TestValidateGenesisAcceptsClean(t *testing.T) {
gs := types.GenesisState{
Pacts: []types.Pact{
{PactID: "p1", Type: types.PactPause, Status: types.StatusProposed},
{PactID: "p2", Type: types.PactCover, Status: types.StatusActive},
},
}
bz, _ := json.Marshal(gs)
if err := types.ValidateGenesis(bz); err != nil {
t.Errorf("ValidateGenesis should accept clean genesis, got: %v", err)
}
}
// TestMissionLockCheckIsNoOp asserts the genesis-side MissionLockCheck helper
// is a no-op (the const flags are the true firewall). It must return nil for
// any slice of Pacts — the Mission Lock is enforced at compile time by the
// const bools, not at genesis load.
func TestMissionLockCheckIsNoOp(t *testing.T) {
pacts := []types.Pact{
{PactID: "p1", Type: types.PactPause},
{PactID: "p2", Type: types.PactGround},
{PactID: "p3", Type: types.PactStance},
{PactID: "p4", Type: types.PactCover},
}
if err := types.MissionLockCheck(pacts); err != nil {
t.Errorf("MissionLockCheck should be a no-op (const flags are the firewall), got: %v", err)
}
}
// TestModuleConsts asserts the four Cosmos-convention module consts.
func TestModuleConsts(t *testing.T) {
if types.ModuleName != "pact" {
t.Errorf("ModuleName = %q", types.ModuleName)
}
if types.StoreKey != "pact" {
t.Errorf("StoreKey = %q", types.StoreKey)
}
if types.RouterKey != "pact" {
t.Errorf("RouterKey = %q", types.RouterKey)
}
if types.QuerierRoute != "pact" {
t.Errorf("QuerierRoute = %q", types.QuerierRoute)
}
}
// TestDefaultParams asserts DefaultParams returns a zero-value Params.
func TestDefaultParams(t *testing.T) {
_ = types.DefaultParams() // no panics
}
// --- Lexicon assertion (REQ-012) -------------------------------------------------
// TestLexiconNoBannedTermsInPactPackage scans every non-test .go file in
// the pact/types package directory for the 9 banned terms (case-insensitive).
// Production files only — the test file references banned terms via the
// lexicon package helpers (standard lexicon-test bootstrapping pattern;
// no banned literals are inlined in this test file).
func TestLexiconNoBannedTermsInPactPackage(t *testing.T) {
pkgDir := packageDir(t, "github.com/oy/openyield/x/pact/types")
files, err := filepath.Glob(filepath.Join(pkgDir, "*.go"))
if err != nil {
t.Fatalf("glob: %v", err)
}
prodFiles := []string{}
for _, f := range files {
if strings.HasSuffix(f, "_test.go") {
continue
}
prodFiles = append(prodFiles, f)
}
if len(prodFiles) == 0 {
t.Fatal("no production .go files found in pact/types")
}
for _, f := range prodFiles {
bz, err := os.ReadFile(f)
if err != nil {
t.Fatalf("read %s: %v", f, err)
}
if found, ok := lexicon.FindBannedTerm(string(bz)); ok {
t.Errorf("%s: banned term %q (REQ-012 lexicon firewall)", filepath.Base(f), found)
}
}
}
// TestLexiconNoBannedTermsInPactTestFile asserts this test file itself does
// not contain any banned term as a literal (the firewall scans test files
// too; the lexicon helpers must be used rather than inlining banned terms).
// This is the self-bootstrapping check.
func TestLexiconNoBannedTermsInPactTestFile(t *testing.T) {
_, thisFile, _, ok := runtime.Caller(0)
if !ok {
t.Fatal("runtime.Caller failed")
}
bz, err := os.ReadFile(thisFile)
if err != nil {
t.Fatalf("read self: %v", err)
}
if found, ok := lexicon.FindBannedTerm(string(bz)); ok {
t.Fatalf("pact test file contains banned term %q — use lexicon helpers, not literals", found)
}
}
// packageDir resolves a Go import path to its filesystem directory by
// walking up from this test file (v0.2 skeleton has zero external deps).
func packageDir(t *testing.T, importPath string) string {
t.Helper()
_, file, _, ok := runtime.Caller(0)
if !ok {
t.Fatal("runtime.Caller failed")
}
// file = .../oy/x/pact/types/types_test.go -> repoRoot = .../oy (4 dirs up)
repoRoot := filepath.Dir(filepath.Dir(filepath.Dir(filepath.Dir(file))))
rel := strings.TrimPrefix(importPath, "github.com/oy/openyield/")
return filepath.Join(repoRoot, rel)
}