Files
story/internal/contractgovernance/device_contract_test.go
T

91 lines
3.0 KiB
Go
Raw Normal View History

2026-08-26 06:41:07 +08:00
package contractgovernance
import (
"io/fs"
"os"
"path/filepath"
"regexp"
"runtime"
"strings"
"testing"
)
func TestDeviceContractCompatibilityGuardrails(t *testing.T) {
root := repositoryRoot(t)
contract := readFile(t, filepath.Join(root, "openapi", "openapi.yaml"))
versions := regexp.MustCompile(`(?m)^ /device/v([0-9]+)(?:/[^:]+)?:$`).FindAllStringSubmatch(contract, -1)
if len(versions) < 10 {
t.Fatalf("found only %d versioned device paths; contract scan likely stopped matching", len(versions))
}
for _, match := range versions {
if match[1] != "2" {
t.Fatalf("device path %q bypasses the current v2 compatibility baseline", match[0])
}
}
for _, required := range []string{
"rtc_credential_renewal:",
"省略时保持旧版v2终端行为",
"设备不得据此改变采集、编码、发布、订阅或播放逻辑",
} {
if !strings.Contains(contract, required) {
t.Fatalf("OpenAPI is missing device compatibility rule %q", required)
}
}
contractVersion := regexp.MustCompile(`(?m)^ version: ([0-9]+\.[0-9]+\.[0-9]+)$`).FindStringSubmatch(contract)
if len(contractVersion) != 2 {
t.Fatal("OpenAPI info.version must be a semantic version")
}
notes := readFile(t, filepath.Join(root, "docs", "device-api-compatibility.md"))
if !strings.Contains(notes, "当前接口契约:OpenAPI "+contractVersion[1]) || !strings.Contains(notes, "/device/v2") || !strings.Contains(notes, "/device/v3") {
t.Fatal("device compatibility notes are not synchronized with the current contract and next breaking version")
}
}
func TestDeviceBehaviorDoesNotDependOnUserAgentOrEngineMode(t *testing.T) {
root := repositoryRoot(t)
for _, relative := range []string{"internal/deviceauth", "internal/httpapi", "internal/orchestration", "cmd/sim-device"} {
directory := filepath.Join(root, filepath.FromSlash(relative))
err := filepath.WalkDir(directory, func(path string, entry fs.DirEntry, walkErr error) error {
if walkErr != nil {
return walkErr
}
if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".go") || strings.HasSuffix(entry.Name(), "_test.go") {
return nil
}
contents := readFile(t, path)
for _, forbidden := range []string{`Header.Get("User-Agent")`, ".UserAgent()"} {
if strings.Contains(contents, forbidden) {
t.Errorf("%s uses firmware User-Agent for behavior selection", filepath.ToSlash(path))
}
}
if relative == "cmd/sim-device" && strings.Contains(contents, "engine_mode") {
t.Errorf("%s makes the terminal aware of diagnostic-only engine_mode", filepath.ToSlash(path))
}
return nil
})
if err != nil {
t.Fatalf("scan %s: %v", relative, err)
}
}
}
func repositoryRoot(t *testing.T) string {
t.Helper()
_, current, _, ok := runtime.Caller(0)
if !ok {
t.Fatal("resolve contract test location")
}
return filepath.Clean(filepath.Join(filepath.Dir(current), "..", ".."))
}
func readFile(t *testing.T, path string) string {
t.Helper()
contents, err := os.ReadFile(path)
if err != nil {
t.Fatalf("read %s: %v", path, err)
}
return string(contents)
}