diff --git a/.github/workflows/go-ci.yml b/.github/workflows/go-ci.yml index 2566e6c..294f8bd 100644 --- a/.github/workflows/go-ci.yml +++ b/.github/workflows/go-ci.yml @@ -29,3 +29,34 @@ jobs: run: go vet ./... - name: test run: go test ./... + + ontology-integration: + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: ubuntu-latest + env: + FFS0_READ_TOKEN: ${{ secrets.FFS0_READ_TOKEN }} + steps: + - uses: actions/checkout@v4 + - name: Report missing private-repository credential + if: ${{ env.FFS0_READ_TOKEN == '' }} + run: echo '::warning::FFS0_READ_TOKEN is not configured; real-ontology integration tests were not run.' + - name: Checkout private ontology source + if: ${{ env.FFS0_READ_TOKEN != '' }} + uses: actions/checkout@v4 + with: + repository: Collider-Data-Systems/ffs0 + path: ffs0 + token: ${{ env.FFS0_READ_TOKEN }} + sparse-checkout: kb/superset/ontology.json + sparse-checkout-cone-mode: false + - uses: actions/setup-go@v5 + if: ${{ env.FFS0_READ_TOKEN != '' }} + with: + go-version-file: go.mod + cache: true + - name: Test with real ontology + if: ${{ env.FFS0_READ_TOKEN != '' }} + env: + MOOS_INTEGRATION: "1" + MOOS_ONTOLOGY_PATH: ${{ github.workspace }}/ffs0/kb/superset/ontology.json + run: go test ./... diff --git a/internal/operad/integration_ontology_test.go b/internal/operad/integration_ontology_test.go new file mode 100644 index 0000000..622aaea --- /dev/null +++ b/internal/operad/integration_ontology_test.go @@ -0,0 +1,29 @@ +package operad + +import ( + "os" + "testing" +) + +func integrationOntologyPath(t *testing.T) string { + t.Helper() + + if path := os.Getenv("MOOS_ONTOLOGY_PATH"); path != "" { + if _, err := os.Stat(path); err != nil { + t.Fatalf("MOOS_ONTOLOGY_PATH=%q is not readable: %v", path, err) + } + return path + } + + candidates := []string{ + "../../../ffs0/kb/superset/ontology.json", + "../../../../ffs0/kb/superset/ontology.json", + } + for _, path := range candidates { + if _, err := os.Stat(path); err == nil { + return path + } + } + t.Fatalf("MOOS_INTEGRATION=1 but ontology.json not found; set MOOS_ONTOLOGY_PATH or provide one of %v", candidates) + return "" +} diff --git a/internal/operad/loader_port_pairs_test.go b/internal/operad/loader_port_pairs_test.go index 4437078..c5fa205 100644 --- a/internal/operad/loader_port_pairs_test.go +++ b/internal/operad/loader_port_pairs_test.go @@ -120,20 +120,7 @@ func TestLoadRegistry_LoadsRealOntology_WF19Pairs(t *testing.T) { if os.Getenv("MOOS_INTEGRATION") != "1" { t.Skip("set MOOS_INTEGRATION=1 to run ontology integration check") } - candidates := []string{ - "../../../ffs0/kb/superset/ontology.json", - "../../../../ffs0/kb/superset/ontology.json", - } - var path string - for _, c := range candidates { - if _, err := os.Stat(c); err == nil { - path = c - break - } - } - if path == "" { - t.Fatalf("MOOS_INTEGRATION=1 but ontology.json not found at %v", candidates) - } + path := integrationOntologyPath(t) reg, err := LoadRegistry(path) if err != nil { diff --git a/internal/operad/member_of_pair_test.go b/internal/operad/member_of_pair_test.go index c5c18b5..1a39f67 100644 --- a/internal/operad/member_of_pair_test.go +++ b/internal/operad/member_of_pair_test.go @@ -172,20 +172,7 @@ func TestLoadRegistry_DeclaredPairsAllColored_Integration(t *testing.T) { if os.Getenv("MOOS_INTEGRATION") != "1" { t.Skip("set MOOS_INTEGRATION=1 to run the declared-vs-loaded ontology readback") } - candidates := []string{ - "../../../ffs0/kb/superset/ontology.json", - "../../../../ffs0/kb/superset/ontology.json", - } - var path string - for _, c := range candidates { - if _, err := os.Stat(c); err == nil { - path = c - break - } - } - if path == "" { - t.Fatalf("MOOS_INTEGRATION=1 but ontology.json not found at %v", candidates) - } + path := integrationOntologyPath(t) reg, err := LoadRegistry(path) if err != nil { diff --git a/internal/operad/occupancy_test.go b/internal/operad/occupancy_test.go index d2dc380..00c588c 100644 --- a/internal/operad/occupancy_test.go +++ b/internal/operad/occupancy_test.go @@ -165,6 +165,29 @@ func TestCheckAdminCapability_UserActor(t *testing.T) { } } +func TestCheckAdminCapability_WidenedGovernsSet(t *testing.T) { + state := stateWithAdminChain() + for _, agentURN := range []graph.URN{ + "urn:moos:agent:claude-cowork.hp-laptop", + "urn:moos:agent:vscode.hp-laptop.copilot", + } { + state.Nodes[agentURN] = graph.Node{URN: agentURN, TypeID: "agent"} + relationURN := graph.URN("urn:moos:rel:sam.governs." + string(agentURN)) + state.Relations[relationURN] = graph.Relation{ + URN: relationURN, + RewriteCategory: graph.WF02, + SrcURN: "urn:moos:user:sam", + SrcPort: "governs", + TgtURN: agentURN, + TgtPort: "governed-by", + } + } + + if !CheckAdminCapability(state, "urn:moos:session:sam.hp-laptop") { + t.Error("expected D10 agent governance links to coexist with the superadmin grant") + } +} + func TestCheckAdminCapability_GroupActor(t *testing.T) { state := stateWithAdminChain() state.Nodes["urn:moos:group:sam"] = graph.Node{URN: "urn:moos:group:sam", TypeID: "group"} diff --git a/internal/operad/validate_color_gate_test.go b/internal/operad/validate_color_gate_test.go index 56878ea..358112e 100644 --- a/internal/operad/validate_color_gate_test.go +++ b/internal/operad/validate_color_gate_test.go @@ -2,7 +2,6 @@ package operad import ( "os" - "path/filepath" "strings" "testing" @@ -274,19 +273,7 @@ func TestIntegration_ColorGate_CoversAllDeclaredOntologyPairs(t *testing.T) { if os.Getenv("MOOS_INTEGRATION") != "1" { t.Skip("set MOOS_INTEGRATION=1 to run against the sibling ffs0 ontology") } - var path string - for _, cand := range []string{ - filepath.Join("..", "..", "..", "ffs0", "kb", "superset", "ontology.json"), - filepath.Join("..", "..", "..", "..", "ffs0", "kb", "superset", "ontology.json"), - } { - if _, err := os.Stat(cand); err == nil { - path = cand - break - } - } - if path == "" { - t.Fatalf("MOOS_INTEGRATION=1 set but sibling ffs0 ontology.json not found") - } + path := integrationOntologyPath(t) reg, err := LoadRegistry(path) if err != nil { t.Fatalf("LoadRegistry(%s): %v", path, err)