first commit

This commit is contained in:
npc0-hue
2026-07-11 14:56:10 +08:00
commit 7e05d0a4e7
660 changed files with 78119 additions and 0 deletions
+221
View File
@@ -0,0 +1,221 @@
package validator
import (
"strings"
"testing"
"browser.local/platform/domain"
)
func TestValidateAIProviderRejectsRawSecret(t *testing.T) {
provider := validAIProvider()
provider.APIKeyRef = "sk-test-secret"
err := ValidateAIProvider(provider)
if err == nil || !strings.Contains(err.Error(), "apiKeyRef must reference secret storage") {
t.Fatalf("expected raw secret rejection, got %v", err)
}
}
func TestValidateAIProviderRequiresDefaultModelInModels(t *testing.T) {
provider := validAIProvider()
provider.DefaultModel = "missing-model"
err := ValidateAIProvider(provider)
if err == nil || !strings.Contains(err.Error(), "defaultModel must be included") {
t.Fatalf("expected default model validation, got %v", err)
}
}
func TestValidateGamePluginManifestRegistration(t *testing.T) {
registration := validGamePluginManifestRegistration()
if err := ValidateGamePluginManifestRegistration(registration); err != nil {
t.Fatalf("expected manifest registration to validate, got %v", err)
}
}
func TestValidateGamePluginManifestRegistrationRejectsUnsafeRequests(t *testing.T) {
registration := validGamePluginManifestRegistration()
registration.Manifest.Description = "requires direct run socket and raw AI key material"
registration.ManifestRef = "file:///etc/plugin.json"
err := ValidateGamePluginManifestRegistration(registration)
if err == nil {
t.Fatal("expected unsafe manifest registration rejection")
}
message := err.Error()
for _, want := range []string{"manifestRef is unsafe", "direct run access", "raw credential or AI/provider key"} {
if !strings.Contains(message, want) {
t.Fatalf("expected validation error to contain %q, got %v", want, err)
}
}
}
func TestValidateGamePluginManifestRegistrationRejectsUnsafeCapabilitiesAndPermissions(t *testing.T) {
registration := validGamePluginManifestRegistration()
registration.Manifest.Capabilities = append(registration.Manifest.Capabilities, "run.socket")
registration.Manifest.Permissions = append(registration.Manifest.Permissions, "provider.key.read")
err := ValidateGamePluginManifestRegistration(registration)
if err == nil || !strings.Contains(err.Error(), "manifest.capabilities") || !strings.Contains(err.Error(), "permissions") {
t.Fatalf("expected unsafe capability and permission rejection, got %v", err)
}
}
func TestValidateServerInstanceDependencies(t *testing.T) {
instance := domain.ServerInstance{
ID: "server-1",
PluginID: "server.scum",
PluginVersion: "1.0.0",
RunEndpointID: "run-local",
Name: "SCUM #1",
State: domain.ServerInstanceStateDraft,
ConfigVersion: 1,
}
plugin := domain.GamePlugin{
ID: "server.scum",
Version: "1.0.0",
RequiredRunCapabilities: []string{"process.start", "logs.read"},
Status: domain.GamePluginStatusInstalled,
}
endpoint := domain.RunEndpoint{
ID: "run-local",
Status: domain.RunEndpointStatusOnline,
Capabilities: []string{"process.start", "logs.read", "files.read"},
}
if err := ValidateServerInstanceDependencies(instance, plugin, endpoint); err != nil {
t.Fatalf("expected valid dependencies, got %v", err)
}
endpoint.Capabilities = []string{"process.start"}
err := ValidateServerInstanceDependencies(instance, plugin, endpoint)
if err == nil || !strings.Contains(err.Error(), "logs.read") {
t.Fatalf("expected missing capability error, got %v", err)
}
}
func TestValidateServerInstanceRejectsDeletedCreateState(t *testing.T) {
instance := domain.ServerInstance{
ID: "server-1",
PluginID: "server.scum",
PluginVersion: "1.0.0",
RunEndpointID: "run-local",
Name: "SCUM #1",
State: domain.ServerInstanceStateDeleted,
}
err := ValidateServerInstance(instance)
if err == nil || !strings.Contains(err.Error(), "state must not be deleted") {
t.Fatalf("expected deleted state rejection, got %v", err)
}
}
func TestValidateJobBoundsProgress(t *testing.T) {
job := domain.Job{
ID: "job-1",
RunEndpointID: "run-local",
Capability: "process.start",
IdempotencyKey: "idem-1",
State: domain.JobStateQueued,
Progress: domain.JobProgress{Percent: 101},
}
err := ValidateJob(job)
if err == nil || !strings.Contains(err.Error(), "progress.percent") {
t.Fatalf("expected progress bounds error, got %v", err)
}
}
func TestValidateArtifactLogAndAudit(t *testing.T) {
artifact := domain.Artifact{
ID: "artifact-1",
OwnerKind: domain.ArtifactOwnerKindJob,
OwnerID: "job-1",
SizeBytes: 10,
Checksum: "sha256:abc",
State: domain.ArtifactStateAvailable,
}
if err := ValidateArtifact(artifact); err != nil {
t.Fatalf("expected artifact to validate, got %v", err)
}
stream := domain.LogStream{
ID: "log-1",
ServerInstanceID: "server-1",
Source: domain.LogStreamSourceFile,
StreamKey: "server.log",
StorageBackend: domain.LogStorageBackendLocalSegments,
RetentionPolicy: "default",
}
if err := ValidateLogStream(stream); err != nil {
t.Fatalf("expected log stream to validate, got %v", err)
}
audit := domain.AuditEvent{
ID: "audit-1",
ActorID: "user-1",
Action: "server.create",
ResourceKind: "server-instance",
ResourceID: "server-1",
Result: domain.AuditResultSuccess,
Summary: "created server instance",
}
if err := ValidateAuditEvent(audit); err != nil {
t.Fatalf("expected audit event to validate, got %v", err)
}
audit.Summary = "bearer raw-secret"
if err := ValidateAuditEvent(audit); err == nil || !strings.Contains(err.Error(), "summary must be redacted") {
t.Fatalf("expected audit redaction error, got %v", err)
}
}
func validAIProvider() domain.AIProvider {
return domain.AIProvider{
ID: "ai.openai",
Name: "OpenAI",
Kind: domain.AIProviderKindOpenAI,
BaseURL: "https://api.openai.com/v1",
APIKeyRef: "secret://providers/openai",
Models: []string{"gpt-4.1", "gpt-4.1-mini"},
DefaultModel: "gpt-4.1",
RelayMode: domain.AIRelayModeDirect,
TimeoutMS: 30000,
Status: domain.AIProviderStatusActive,
RedactionPolicy: "default",
}
}
func validGamePluginManifestRegistration() domain.GamePluginManifestRegistration {
return domain.GamePluginManifestRegistration{
ManifestRef: "artifact://manifests/game.example/0.1.0",
Manifest: domain.GamePluginManifest{
ID: "game.example",
Name: "Example Server",
Description: "Development plugin",
Version: "0.1.0",
Kind: "game-plugin",
Tags: []string{"example", "development"},
Server: domain.GamePluginManifestServer{
Type: "example",
DisplayName: "Example Server",
SupportedOS: []string{"linux", "darwin"},
CreateFormSchema: "schemas/create-form.schema.json",
},
Capabilities: []string{"process.install", "process.start", "process.stop", "logs.read", "files.read", "artifacts.read", "ai.invoke"},
Permissions: []string{"server.read", "server.lifecycle", "server.logs.read", "server.files.read", "server.artifacts.read", "ai.invoke"},
Actions: domain.PluginLifecycleActions{
Install: "actions/install.json",
Start: "actions/start.json",
Stop: "actions/stop.json",
Restart: "actions/restart.json",
},
Pages: []domain.GamePluginPage{
{Key: "logs", Title: "Logs", Path: "/logs", Permissions: []string{"server.logs.read"}},
},
AI: domain.GamePluginManifestAI{Purposes: []string{"logs.diagnose"}},
},
}
}