fluid-telegram/internal/adaptercfg/adaptercfg_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

114 lines
3.4 KiB
Go

package adaptercfg
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
"github.com/tegwick/fluid-telegram/internal/secrets"
"github.com/tegwick/fluid-telegram/internal/spec"
"github.com/tegwick/fluid-telegram/internal/state"
)
func provisioned() *state.Resolved {
now := time.Now()
return &state.Resolved{
Campaign: "hall-of-helix",
Bot: state.Bot{Username: "HelixForgeBot", ID: 42},
Channels: map[string]state.Channel{
spec.Test: {ChatID: -1001, AdminRights: []string{spec.PostMessages}, TestPublicationAt: &now},
spec.Live: {ChatID: -2002, Username: "hallofhelix", AdminRights: []string{spec.PostMessages}},
},
}
}
func TestFromResolvedPicksOneChannel(t *testing.T) {
cfg, err := FromResolved(provisioned(), spec.Test, "bao:platform/x/bot-token")
if err != nil {
t.Fatal(err)
}
if cfg.ChatID != -1001 || cfg.Channel != spec.Test {
t.Fatalf("got %+v", cfg)
}
cfg, err = FromResolved(provisioned(), spec.Live, "bao:platform/x/bot-token")
if err != nil {
t.Fatal(err)
}
if cfg.ChatID != -2002 {
t.Fatalf("public chat id = %d", cfg.ChatID)
}
}
func TestFromResolvedRefusesUnprovisioned(t *testing.T) {
if _, err := FromResolved(&state.Resolved{}, spec.Test, "ref"); err == nil {
t.Fatal("accepted an empty state")
}
rs := provisioned()
delete(rs.Channels, spec.Live)
rs.Channels[spec.Test] = state.Channel{ChatID: -1001, AdminRights: []string{spec.PostMessages}}
if _, err := FromResolved(rs, spec.Live, "ref"); err == nil {
t.Fatal("accepted a public channel that has not been provisioned")
}
}
func TestEnvFileHasNoToken(t *testing.T) {
cfg, _ := FromResolved(provisioned(), spec.Test, "bao:platform/x/bot-token")
out := cfg.EnvFile()
for _, line := range strings.Split(out, "\n") {
if strings.HasPrefix(line, "TELEGRAM_BOT_TOKEN=") {
t.Fatal("env file emitted a token value")
}
}
if !strings.Contains(out, "TELEGRAM_CHANNEL_ID=-1001") {
t.Fatalf("missing chat id:\n%s", out)
}
if !strings.Contains(out, "TELEGRAM_BOT_TOKEN_REF=bao:platform/x/bot-token") {
t.Fatalf("missing token ref:\n%s", out)
}
if strings.Contains(out, "operator-session") {
t.Fatal("adapter config mentioned the operator session")
}
}
func TestLoadTokenReadsOnlyTheBotToken(t *testing.T) {
var requested []string
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
requested = append(requested, r.URL.Path)
if strings.Contains(r.URL.Path, secrets.KeyOperatorSession) {
t.Errorf("adapter requested the operator session: %s", r.URL.Path)
w.WriteHeader(http.StatusForbidden)
return
}
if !strings.HasSuffix(r.URL.Path, "/"+secrets.KeyBotToken) {
t.Errorf("unexpected path %s", r.URL.Path)
w.WriteHeader(http.StatusNotFound)
return
}
json.NewEncoder(w).Encode(map[string]any{
"data": map[string]any{"data": map[string]string{"token": "123:abc"}},
})
}))
t.Cleanup(srv.Close)
t.Setenv("BAO_ADDR", srv.URL)
t.Setenv("BAO_TOKEN", "test-token")
store, err := secrets.NewFromEnv("hall-of-helix")
if err != nil {
t.Fatal(err)
}
got, err := LoadToken(context.Background(), store)
if err != nil {
t.Fatal(err)
}
if got != "123:abc" {
t.Fatalf("token = %q", got)
}
for _, p := range requested {
if strings.Contains(p, secrets.KeyOperatorSession) || strings.Contains(p, secrets.KeyOperatorApp) {
t.Fatalf("adapter store was asked for %s", p)
}
}
}