fluid-telegram/internal/spec/spec_test.go
tegwick 1e9481e108 Finish the provisioner's remaining plane: honest check, adapter config, IEI-2
plan --check treated BotFather profile reassertion as drift, so a scheduled
check could never go quiet. Reassertion is now its own kind; a changed spec
digest is the drift signal. apply reasserts the profile, updates channel copy
when the spec changes, and is tested against a fake Telegram surface.

The adapter reads its chat id from resolved state and its token from OpenBao,
never the operator session. IEI-2 records that an approved provisioning plan
is not autonomous Daimon action.

Assistant: grok
Assistant-Session: 01a09d5a-92c1-7802-915b-92bee57fc9f0
2026-09-14 02:55:58 +02:00

174 lines
5 KiB
Go

package spec
import (
"os"
"path/filepath"
"strings"
"testing"
)
// The real specs must load. If either stops validating, the tool has drifted
// away from the artifacts it exists to read.
func TestRealSpecsLoad(t *testing.T) {
for _, p := range []string{
"../../presence/telegram.example.yaml",
"../../../pr-hall-of-helix/presence/telegram.yaml",
} {
if _, err := os.Stat(p); err != nil {
t.Skipf("not present in this checkout: %s", p)
}
got, digest, err := Load(p)
if err != nil {
t.Fatalf("%s: %v", p, err)
}
if got.Campaign == "" || !strings.HasPrefix(digest, "sha256:") {
t.Fatalf("%s: campaign=%q digest=%q", p, got.Campaign, digest)
}
}
}
func base() Presence {
return Presence{
SchemaVersion: "0.1",
Campaign: "hall-of-helix",
Interface: "helix-forge-telegram-publishing",
Bot: Bot{
Name: "HelixForge",
UsernamePreference: []string{"HelixForgeBot"},
About: "about",
Description: "description",
},
Channels: map[string]Channel{
Test: {Title: "t", Visibility: Private, AdminRights: []string{PostMessages}},
Live: {Title: "p", Visibility: Public, UsernamePreference: []string{"hallofhelix"},
AdminRights: []string{PostMessages}},
},
}
}
// Every rule the schema states, asserted here too. These are the cases the
// schema file rejects; if this table and presence/telegram.schema.yaml ever
// disagree, one of them is wrong and a spec will pass one gate and fail another.
func TestValidateRejects(t *testing.T) {
cases := []struct {
name string
mutate func(*Presence)
want string
}{
{"extra admin right", func(p *Presence) {
c := p.Channels[Live]
c.AdminRights = []string{PostMessages, "can_delete_messages"}
p.Channels[Live] = c
}, "can_delete_messages"},
{"wrong admin right", func(p *Presence) {
c := p.Channels[Live]
c.AdminRights = []string{"can_delete_messages"}
p.Channels[Live] = c
}, "refused"},
{"private channel with username", func(p *Presence) {
c := p.Channels[Test]
c.UsernamePreference = []string{"secretchan"}
p.Channels[Test] = c
}, "must not declare a username"},
{"public channel without username", func(p *Presence) {
c := p.Channels[Live]
c.UsernamePreference = nil
p.Channels[Live] = c
}, "must declare at least one"},
{"bot username not ending in bot", func(p *Presence) {
p.Bot.UsernamePreference = []string{"HelixForge"}
}, `end in "bot"`},
{"no bot username candidates", func(p *Presence) {
p.Bot.UsernamePreference = nil
}, "at least one candidate"},
{"missing test channel", func(p *Presence) {
delete(p.Channels, Test)
}, "channels.test is required"},
{"unknown channel key", func(p *Presence) {
p.Channels["archive"] = Channel{Title: "a", Visibility: Private}
}, "not a known channel"},
{"bad visibility", func(p *Presence) {
c := p.Channels[Test]
c.Visibility = "unlisted"
p.Channels[Test] = c
}, "visibility must be"},
{"wrong schema version", func(p *Presence) {
p.SchemaVersion = "0.2"
}, "schema_version"},
{"campaign not a slug", func(p *Presence) {
p.Campaign = "Hall Of Helix"
}, "lowercase slug"},
{"about too long", func(p *Presence) {
p.Bot.About = strings.Repeat("x", 121)
}, "limit is 120"},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
p := base()
tc.mutate(&p)
err := p.Validate()
if err == nil {
t.Fatalf("accepted a spec that must be rejected")
}
if !strings.Contains(err.Error(), tc.want) {
t.Fatalf("error did not mention %q:\n%v", tc.want, err)
}
})
}
}
func TestValidateAcceptsBase(t *testing.T) {
p := base()
if err := p.Validate(); err != nil {
t.Fatalf("rejected a valid spec: %v", err)
}
}
// An unknown field is a mistake about what is being declared, not a comment.
func TestUnknownFieldRejected(t *testing.T) {
dir := t.TempDir()
f := filepath.Join(dir, "s.yaml")
os.WriteFile(f, []byte(`
presence:
schema_version: "0.1"
campaign: "c"
interface: "i"
pinned: true
bot: {name: "n", username_preference: ["aBot"], about: "a", description: "d"}
channels:
test: {title: "t", visibility: private}
public: {title: "p", visibility: public, username_preference: ["abcdef"]}
`), 0o600)
if _, _, err := Load(f); err == nil {
t.Fatal("accepted an unknown field")
}
}
// The Go validator mirrors presence/telegram.schema.yaml. If a rule leaves the
// schema file, a spec can pass one gate and fail the other.
func TestSchemaStillStatesTheClamp(t *testing.T) {
raw, err := os.ReadFile("../../presence/telegram.schema.yaml")
if err != nil {
t.Fatal(err)
}
s := string(raw)
for _, want := range []string{
"additionalProperties: false",
"const: post_messages",
"must not declare",
"maxItems: 1",
} {
if !strings.Contains(s, want) {
t.Errorf("schema no longer states %q", want)
}
}
}
// Rights are the tool's decision, not the spec's.
func TestRightsAlwaysClamped(t *testing.T) {
c := Channel{AdminRights: nil}
got := c.Rights()
if len(got) != 1 || got[0] != PostMessages {
t.Fatalf("Rights() = %v, want [%s]", got, PostMessages)
}
}