134 lines
4.1 KiB
Go
134 lines
4.1 KiB
Go
package settings
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestDetermineState(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
in Snapshot
|
|
want SystemState
|
|
}{
|
|
{"empty", Snapshot{}, StateUninitialized},
|
|
{"partial", Snapshot{ClientID: "id"}, StateCredentialError},
|
|
{"saved", Snapshot{ClientID: "id", SecretConfigured: true}, StateCredentialsSaved},
|
|
{"verified", Snapshot{ClientID: "id", SecretConfigured: true, CredentialsVerified: true}, StateWaitingAdmin},
|
|
{"active", Snapshot{ClientID: "id", SecretConfigured: true, CredentialsVerified: true, AdministratorCount: 1}, StateActive},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if got := DetermineState(tc.in); got != tc.want {
|
|
t.Fatalf("got %s want %s", got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseCommandStrict(t *testing.T) {
|
|
yes := []string{"/setting", "/设置", "/setting 123456", "/设置 123456", "/setting language ja", "/设置 language ja", "/setting support", "/设置 question https://alidocs.dingtalk.com/i/nodes/abc"}
|
|
for _, raw := range yes {
|
|
if c, ok := ParseCommand(raw); !ok || c.Name == "" {
|
|
t.Fatalf("must match %q", raw)
|
|
}
|
|
}
|
|
no := []string{"/help", "//help", "/settings", "//setting", "如何设置机器人", "abc /setting", "请解释/setting", "/settiing"}
|
|
for _, raw := range no {
|
|
if _, ok := ParseCommand(raw); ok {
|
|
t.Fatalf("must not match %q", raw)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseNotableLink(t *testing.T) {
|
|
got, err := ParseNotableLink("https://alidocs.dingtalk.com/i/nodes/base123?sheetId=sheet456&viewId=view789&recordId=row")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got.BaseID != "base123" || got.SheetID != "sheet456" || got.ViewID != "view789" {
|
|
t.Fatalf("got %+v", got)
|
|
}
|
|
if _, err = ParseNotableLink("http://evil.example/x?sheetId=s"); err == nil {
|
|
t.Fatal("expected rejection")
|
|
}
|
|
}
|
|
|
|
func TestParseNotableLinkAcceptsTrustedHostRelativeTail(t *testing.T) {
|
|
for _, raw := range []string{
|
|
"i/nodes/base123?sheetId=sheet456&viewId=view789&recordId=row",
|
|
"/i/nodes/base123?sheetId=sheet456&viewId=view789&recordId=row",
|
|
} {
|
|
got, err := ParseNotableLink(raw)
|
|
if err != nil {
|
|
t.Fatalf("ParseNotableLink(%q): %v", raw, err)
|
|
}
|
|
if got.BaseID != "base123" || got.SheetID != "sheet456" || got.ViewID != "view789" {
|
|
t.Fatalf("ParseNotableLink(%q)=%+v", raw, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseNotableLinkDecodesSheetAndViewFromIframeQuery(t *testing.T) {
|
|
got, err := ParseNotableLink("i/nodes/base123?iframeQuery=entrance%3Ddata%26sheetId%3Dsheet456%26viewId%3Dview789")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got.BaseID != "base123" || got.SheetID != "sheet456" || got.ViewID != "view789" {
|
|
t.Fatalf("got=%+v", got)
|
|
}
|
|
}
|
|
|
|
func TestParseNotableLinkRejectsConflictingIframeQueryIDs(t *testing.T) {
|
|
_, err := ParseNotableLink("i/nodes/base123?sheetId=sheet-top&iframeQuery=sheetId%3Dsheet-nested%26viewId%3Dview789")
|
|
if err == nil || !strings.Contains(err.Error(), "conflicting") {
|
|
t.Fatalf("err=%v", err)
|
|
}
|
|
}
|
|
|
|
func TestParseNotableLinkRejectsUnsafeOrAmbiguousRelativeTail(t *testing.T) {
|
|
for _, raw := range []string{
|
|
"//evil.example/i/nodes/base?sheetId=sheet",
|
|
"evil.example/i/nodes/base?sheetId=sheet",
|
|
"../i/nodes/base?sheetId=sheet",
|
|
"i/nodes/../base?sheetId=sheet",
|
|
"i/nodes/base",
|
|
"https://evil.example/i/nodes/base?sheetId=sheet",
|
|
} {
|
|
if _, err := ParseNotableLink(raw); err == nil {
|
|
t.Fatalf("ParseNotableLink(%q) must reject", raw)
|
|
}
|
|
}
|
|
}
|
|
|
|
type fakeRuntime struct {
|
|
active string
|
|
starts []string
|
|
fail string
|
|
}
|
|
|
|
func (f *fakeRuntime) Start(_ context.Context, id string) error {
|
|
f.starts = append(f.starts, id)
|
|
if id == f.fail {
|
|
return errors.New("boom")
|
|
}
|
|
f.active = id
|
|
return nil
|
|
}
|
|
func (f *fakeRuntime) Stop(_ context.Context) error { f.active = ""; return nil }
|
|
func TestSwitcherRollsBack(t *testing.T) {
|
|
f := &fakeRuntime{active: "old", fail: "new"}
|
|
s := NewSwitcher(f, "old")
|
|
if err := s.Switch(context.Background(), "new"); err == nil {
|
|
t.Fatal("expected error")
|
|
}
|
|
if f.active != "old" {
|
|
t.Fatalf("active=%q", f.active)
|
|
}
|
|
if len(f.starts) != 2 || f.starts[0] != "new" || f.starts[1] != "old" {
|
|
t.Fatalf("starts=%v", f.starts)
|
|
}
|
|
}
|