package grpcserver import ( "testing" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" pb "git.wntrmute.dev/kyle/mcr/gen/mcr/v1" "git.wntrmute.dev/kyle/mcr/internal/policy" ) type fakePolicyReloader struct { reloadCount int } func (f *fakePolicyReloader) Reload(_ policy.RuleStore) error { f.reloadCount++ return nil } func TestCreatePolicyRule(t *testing.T) { deps := adminDeps(t) reloader := &fakePolicyReloader{} deps.Engine = reloader cc := startTestServer(t, deps) client := pb.NewPolicyServiceClient(cc) resp, err := client.CreatePolicyRule(adminCtx(), &pb.CreatePolicyRuleRequest{ Priority: 10, Description: "allow pull for all", Effect: "allow", Actions: []string{"registry:pull"}, Enabled: true, }) if err != nil { t.Fatalf("CreatePolicyRule: %v", err) } if resp.GetId() == 0 { t.Fatal("expected non-zero ID") } if resp.GetDescription() != "allow pull for all" { t.Fatalf("description: got %q, want %q", resp.Description, "allow pull for all") } if resp.GetEffect() != "allow" { t.Fatalf("effect: got %q, want %q", resp.Effect, "allow") } if !resp.GetEnabled() { t.Fatal("expected enabled=true") } if reloader.reloadCount != 1 { t.Fatalf("reloadCount: got %d, want 1", reloader.reloadCount) } } func TestCreatePolicyRuleValidation(t *testing.T) { deps := adminDeps(t) cc := startTestServer(t, deps) client := pb.NewPolicyServiceClient(cc) tests := []struct { name string req *pb.CreatePolicyRuleRequest code codes.Code }{ { name: "zero priority", req: &pb.CreatePolicyRuleRequest{ Priority: 0, Description: "test", Effect: "allow", Actions: []string{"registry:pull"}, }, code: codes.InvalidArgument, }, { name: "empty description", req: &pb.CreatePolicyRuleRequest{ Priority: 1, Effect: "allow", Actions: []string{"registry:pull"}, }, code: codes.InvalidArgument, }, { name: "invalid effect", req: &pb.CreatePolicyRuleRequest{ Priority: 1, Description: "test", Effect: "maybe", Actions: []string{"registry:pull"}, }, code: codes.InvalidArgument, }, { name: "no actions", req: &pb.CreatePolicyRuleRequest{ Priority: 1, Description: "test", Effect: "allow", }, code: codes.InvalidArgument, }, { name: "invalid action", req: &pb.CreatePolicyRuleRequest{ Priority: 1, Description: "test", Effect: "allow", Actions: []string{"registry:fly"}, }, code: codes.InvalidArgument, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { _, err := client.CreatePolicyRule(adminCtx(), tt.req) if err == nil { t.Fatal("expected error") } st, ok := status.FromError(err) if !ok { t.Fatalf("expected gRPC status, got %v", err) } if st.Code() != tt.code { t.Fatalf("code: got %v, want %v", st.Code(), tt.code) } }) } } func TestGetPolicyRule(t *testing.T) { deps := adminDeps(t) cc := startTestServer(t, deps) client := pb.NewPolicyServiceClient(cc) // Create a rule first. created, err := client.CreatePolicyRule(adminCtx(), &pb.CreatePolicyRuleRequest{ Priority: 5, Description: "test rule", Effect: "deny", Actions: []string{"registry:push"}, Enabled: true, }) if err != nil { t.Fatalf("CreatePolicyRule: %v", err) } // Fetch it. got, err := client.GetPolicyRule(adminCtx(), &pb.GetPolicyRuleRequest{Id: created.Id}) if err != nil { t.Fatalf("GetPolicyRule: %v", err) } if got.Id != created.Id { t.Fatalf("id: got %d, want %d", got.Id, created.Id) } if got.Effect != "deny" { t.Fatalf("effect: got %q, want %q", got.Effect, "deny") } } func TestGetPolicyRuleNotFound(t *testing.T) { deps := adminDeps(t) cc := startTestServer(t, deps) client := pb.NewPolicyServiceClient(cc) _, err := client.GetPolicyRule(adminCtx(), &pb.GetPolicyRuleRequest{Id: 99999}) if err == nil { t.Fatal("expected error") } st, ok := status.FromError(err) if !ok { t.Fatalf("expected gRPC status, got %v", err) } if st.Code() != codes.NotFound { t.Fatalf("code: got %v, want NotFound", st.Code()) } } func TestListPolicyRules(t *testing.T) { deps := adminDeps(t) cc := startTestServer(t, deps) client := pb.NewPolicyServiceClient(cc) // Create two rules. for i := range 2 { _, err := client.CreatePolicyRule(adminCtx(), &pb.CreatePolicyRuleRequest{ Priority: int32(i + 1), Description: "rule", Effect: "allow", Actions: []string{"registry:pull"}, Enabled: true, }) if err != nil { t.Fatalf("CreatePolicyRule %d: %v", i, err) } } resp, err := client.ListPolicyRules(adminCtx(), &pb.ListPolicyRulesRequest{}) if err != nil { t.Fatalf("ListPolicyRules: %v", err) } if len(resp.GetRules()) < 2 { t.Fatalf("expected at least 2 rules, got %d", len(resp.Rules)) } } func TestDeletePolicyRule(t *testing.T) { deps := adminDeps(t) reloader := &fakePolicyReloader{} deps.Engine = reloader cc := startTestServer(t, deps) client := pb.NewPolicyServiceClient(cc) // Create then delete. created, err := client.CreatePolicyRule(adminCtx(), &pb.CreatePolicyRuleRequest{ Priority: 1, Description: "to be deleted", Effect: "allow", Actions: []string{"registry:pull"}, Enabled: true, }) if err != nil { t.Fatalf("CreatePolicyRule: %v", err) } initialReloads := reloader.reloadCount _, err = client.DeletePolicyRule(adminCtx(), &pb.DeletePolicyRuleRequest{Id: created.Id}) if err != nil { t.Fatalf("DeletePolicyRule: %v", err) } // Verify it was reloaded. if reloader.reloadCount != initialReloads+1 { t.Fatalf("reloadCount: got %d, want %d", reloader.reloadCount, initialReloads+1) } // Verify it's gone. _, err = client.GetPolicyRule(adminCtx(), &pb.GetPolicyRuleRequest{Id: created.Id}) if err == nil { t.Fatal("expected error after deletion") } st, ok := status.FromError(err) if !ok { t.Fatalf("expected gRPC status, got %v", err) } if st.Code() != codes.NotFound { t.Fatalf("code: got %v, want NotFound", st.Code()) } } func TestDeletePolicyRuleNotFound(t *testing.T) { deps := adminDeps(t) cc := startTestServer(t, deps) client := pb.NewPolicyServiceClient(cc) _, err := client.DeletePolicyRule(adminCtx(), &pb.DeletePolicyRuleRequest{Id: 99999}) if err == nil { t.Fatal("expected error") } st, ok := status.FromError(err) if !ok { t.Fatalf("expected gRPC status, got %v", err) } if st.Code() != codes.NotFound { t.Fatalf("code: got %v, want NotFound", st.Code()) } } func TestUpdatePolicyRule(t *testing.T) { deps := adminDeps(t) reloader := &fakePolicyReloader{} deps.Engine = reloader cc := startTestServer(t, deps) client := pb.NewPolicyServiceClient(cc) // Create a rule. created, err := client.CreatePolicyRule(adminCtx(), &pb.CreatePolicyRuleRequest{ Priority: 10, Description: "original", Effect: "allow", Actions: []string{"registry:pull"}, Enabled: true, }) if err != nil { t.Fatalf("CreatePolicyRule: %v", err) } // Update description. updated, err := client.UpdatePolicyRule(adminCtx(), &pb.UpdatePolicyRuleRequest{ Id: created.Id, Description: "updated description", UpdateMask: []string{"description"}, }) if err != nil { t.Fatalf("UpdatePolicyRule: %v", err) } if updated.Description != "updated description" { t.Fatalf("description: got %q, want %q", updated.Description, "updated description") } // Effect should be unchanged. if updated.Effect != "allow" { t.Fatalf("effect: got %q, want %q", updated.Effect, "allow") } }