Prerequisite reachability filters
**Scope Audited** - Module wiki visibility config and docs. - Toolkit wiki expression engine and visibility tests. - Feat prerequisite rendering/reference behavior. **Changes Made** - Added `hidden_ref(Field, "dataset")` and `all_hidden_refs(..., "dataset")` helpers in [wiki_tables.go](/home/vicky/Projects/nwnee-shadowsoverwestgate/toolkit/internal/topdata/wiki_tables.go:914). - Added feat visibility exclusions in [visibility.yaml](/home/vicky/Projects/nwnee-shadowsoverwestgate/module/topdata/wiki/visibility.yaml:44) for: - `PREREQFEAT1` - `PREREQFEAT2` - `REQSKILL` - `REQSKILL2` - `OrReqFeat0..OrReqFeat4` - Documented the helpers in [README.md](/home/vicky/Projects/nwnee-shadowsoverwestgate/module/topdata/wiki/README.md:53). - Added coverage in [wiki_native_test.go](/home/vicky/Projects/nwnee-shadowsoverwestgate/toolkit/internal/topdata/wiki_native_test.go:646). **Configuration/Schema Impact** - No schema file change. `visibility.yaml` can now use the two new expression helpers. **Compatibility Impact** - Existing visibility expressions are unchanged. - Missing or `****` prerequisite fields are ignored. - Present references that resolve to hidden or missing wiki rows are treated as unreachable. **Tests/Validation** - Confirmed the new test failed before implementation on unknown helper. - Ran `go test ./internal/topdata -run TestWikiVisibility`. - Ran `go test ./internal/topdata`. - Rebuilt toolkit with `./build-tool.sh`. - Ran module wiki build with local toolkit: `SOW_TOOLS_DEV_BINARY=../toolkit/tools/sow-toolkit ./build-wiki.sh` Result: `wiki pages: 771`.
This commit is contained in:
@@ -643,6 +643,90 @@ datasets:
|
||||
}
|
||||
}
|
||||
|
||||
func TestWikiVisibilityCanExcludeUnreachableFeatPrerequisites(t *testing.T) {
|
||||
policy, err := parseWikiVisibilityDefinitions([]byte(`
|
||||
datasets:
|
||||
feat:
|
||||
include:
|
||||
- when: "{{true}}"
|
||||
exclude:
|
||||
- when: "{{hidden_ref(PREREQFEAT1, \"feat\")}}"
|
||||
- when: "{{hidden_ref(REQSKILL, \"skills\")}}"
|
||||
- when: "{{all_hidden_refs(OrReqFeat0, OrReqFeat1, OrReqFeat2, OrReqFeat3, OrReqFeat4, \"feat\")}}"
|
||||
`), "visibility.yaml")
|
||||
if err != nil {
|
||||
t.Fatalf("parse visibility policy: %v", err)
|
||||
}
|
||||
ctx := &wikiContext{
|
||||
visibilityPolicy: &policy,
|
||||
visibleWikiKeys: map[string]bool{
|
||||
"feat:visible": true,
|
||||
"feat:hidden": false,
|
||||
"skills:visible": true,
|
||||
"skills:hidden": false,
|
||||
},
|
||||
featIDToKey: map[int]string{1: "feat:visible", 2: "feat:hidden"},
|
||||
skillIDToKey: map[int]string{1: "skills:visible", 2: "skills:hidden"},
|
||||
}
|
||||
definition := policy.Datasets["feat"]
|
||||
featRow := func(values map[string]any) map[string]any {
|
||||
row := map[string]any{"FEAT": "Test Feat", "DESCRIPTION": "Description"}
|
||||
for key, value := range values {
|
||||
row[key] = value
|
||||
}
|
||||
return row
|
||||
}
|
||||
for name, tt := range map[string]struct {
|
||||
row map[string]any
|
||||
want bool
|
||||
}{
|
||||
"visible required feat": {
|
||||
row: featRow(map[string]any{"PREREQFEAT1": 1}),
|
||||
want: true,
|
||||
},
|
||||
"hidden required feat": {
|
||||
row: featRow(map[string]any{"PREREQFEAT1": 2}),
|
||||
want: false,
|
||||
},
|
||||
"missing required feat is unreachable": {
|
||||
row: featRow(map[string]any{"PREREQFEAT1": 999}),
|
||||
want: false,
|
||||
},
|
||||
"visible required skill": {
|
||||
row: featRow(map[string]any{"REQSKILL": "skills:visible"}),
|
||||
want: true,
|
||||
},
|
||||
"hidden required skill": {
|
||||
row: featRow(map[string]any{"REQSKILL": "skills:hidden"}),
|
||||
want: false,
|
||||
},
|
||||
"null prerequisite fields are ignored": {
|
||||
row: featRow(map[string]any{"PREREQFEAT1": "****", "REQSKILL": "****"}),
|
||||
want: true,
|
||||
},
|
||||
"or group with visible option remains reachable": {
|
||||
row: featRow(map[string]any{"OrReqFeat0": 2, "OrReqFeat1": 1}),
|
||||
want: true,
|
||||
},
|
||||
"or group with only hidden options is unreachable": {
|
||||
row: featRow(map[string]any{"OrReqFeat0": 2, "OrReqFeat1": "feat:missing"}),
|
||||
want: false,
|
||||
},
|
||||
"empty or group is ignored": {
|
||||
row: featRow(map[string]any{"OrReqFeat0": "****", "OrReqFeat1": "****"}),
|
||||
want: true,
|
||||
},
|
||||
} {
|
||||
got, err := ctx.evaluateWikiVisibility("feat", "feat:"+name, tt.row, definition, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("%s visibility evaluation failed: %v", name, err)
|
||||
}
|
||||
if got != tt.want {
|
||||
t.Fatalf("%s expected visibility %v, got %v", name, tt.want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWikiVisibilityMetadataOverrideCannotBypassEligibility(t *testing.T) {
|
||||
policy, err := parseWikiVisibilityDefinitions([]byte(`
|
||||
datasets:
|
||||
|
||||
Reference in New Issue
Block a user