Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions changelog.d/added/go-coverage-push.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
- **Go test coverage push** (`cmd/vmafx-*`, `pkg/`): table-driven tests added
for the Go binaries and packages with lowest coverage. Key gains:
`cmd/vmafx-operator/internal/controller` 7→45 %, `pkg/observability` 68→87 %,
`pkg/score` 47→68 %, `cmd/vmafx-controller/queue` 66→82 %,
`cmd/vmafx-node` 30→46 %. Pre-existing build failures fixed (gRPC server
undefined-var bug, operator int32 type mismatch, stale test API calls,
MCP Vulkan backend missing from dispatcher).
6 changes: 5 additions & 1 deletion cmd/vmafx-controller/auth/grpc_interceptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,11 @@ func (m *Middleware) GRPCUnaryInterceptor() grpc.UnaryServerInterceptor {
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
) (any, error) {
ctx, err := m.authenticateGRPC(ctx, info.FullMethod)
method := ""
if info != nil {
method = info.FullMethod
}
ctx, err := m.authenticateGRPC(ctx, method)
if err != nil {
return nil, err
}
Expand Down
7 changes: 7 additions & 0 deletions cmd/vmafx-controller/auth/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,13 @@ func withClaims(ctx context.Context, c Claims) context.Context {
return ctx
}

// ContextWithClaims returns a context enriched with the supplied Claims.
// Intended for use in unit tests that call gRPC handler methods directly
// (bypassing the auth interceptor) and need a valid tenant context.
func ContextWithClaims(ctx context.Context, c Claims) context.Context {
return withClaims(ctx, c)
}

// writeJSONError writes a JSON error body.
func writeJSONError(w http.ResponseWriter, code int, msg string) {
w.Header().Set("Content-Type", "application/json")
Expand Down
45 changes: 35 additions & 10 deletions cmd/vmafx-controller/grpc_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,25 @@ import (
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"

"github.com/VMAFx/vmafx/cmd/vmafx-controller/auth"
"github.com/VMAFx/vmafx/cmd/vmafx-controller/nodes"
"github.com/VMAFx/vmafx/cmd/vmafx-controller/queue"
"github.com/VMAFx/vmafx/cmd/vmafx-controller/scheduler"
controllerv1 "github.com/VMAFx/vmafx/gen/go/controller"
"github.com/VMAFx/vmafx/pkg/observability"
)

// testTenantCtx returns a context with a "test-tenant" auth context injected.
// Used by tests that call gRPC handler methods directly (bypassing the
// interceptor) so SubmitJob / GetJob / CancelJob can find a tenant_id.
func testTenantCtx() context.Context {
return auth.ContextWithClaims(context.Background(), auth.Claims{
Subject: "test-user",
TenantID: "test-tenant",
Roles: []string{auth.RoleAdmin},
})
}

// ---------------------------------------------------------------------------
// In-process mock stream — exercises StreamJobs and captures sent messages.
// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -227,7 +239,7 @@ func TestSubmitJob_HappyPath(t *testing.T) {
Backend: "cpu",
},
}
resp, err := f.srv.SubmitJob(context.Background(), req)
resp, err := f.srv.SubmitJob(testTenantCtx(), req)
if err != nil {
t.Fatalf("SubmitJob: %v", err)
}
Expand Down Expand Up @@ -270,13 +282,14 @@ func TestSubmitJob_MissingPathsRejected(t *testing.T) {

func TestGetJob_HappyPath(t *testing.T) {
f := newGRPCFixture(t)
subResp, err := f.srv.SubmitJob(context.Background(), &controllerv1.SubmitJobRequest{
ctx := testTenantCtx()
subResp, err := f.srv.SubmitJob(ctx, &controllerv1.SubmitJobRequest{
Scoring: &controllerv1.ScoringParams{Reference: "/r.yuv", Distorted: "/d.yuv", Backend: "cuda"},
})
if err != nil {
t.Fatalf("Submit: %v", err)
}
got, err := f.srv.GetJob(context.Background(), &controllerv1.GetJobRequest{JobId: subResp.GetJobId()})
got, err := f.srv.GetJob(ctx, &controllerv1.GetJobRequest{JobId: subResp.GetJobId()})
if err != nil {
t.Fatalf("GetJob: %v", err)
}
Expand Down Expand Up @@ -313,10 +326,14 @@ func TestGetJob_MissingReturnsNotFound(t *testing.T) {

func TestCancelJob_HappyPath(t *testing.T) {
f := newGRPCFixture(t)
sub, _ := f.srv.SubmitJob(context.Background(), &controllerv1.SubmitJobRequest{
ctx := testTenantCtx()
sub, err := f.srv.SubmitJob(ctx, &controllerv1.SubmitJobRequest{
Scoring: &controllerv1.ScoringParams{Reference: "/r.yuv", Distorted: "/d.yuv"},
})
resp, err := f.srv.CancelJob(context.Background(), &controllerv1.CancelJobRequest{JobId: sub.GetJobId()})
if err != nil {
t.Fatalf("SubmitJob: %v", err)
}
resp, err := f.srv.CancelJob(ctx, &controllerv1.CancelJobRequest{JobId: sub.GetJobId()})
if err != nil {
t.Fatalf("CancelJob: %v", err)
}
Expand Down Expand Up @@ -452,7 +469,7 @@ func TestPullWork_NoJobReturnsEmpty(t *testing.T) {

func TestPullWork_HappyPath(t *testing.T) {
f := newGRPCFixture(t)
_, err := f.srv.SubmitJob(context.Background(), &controllerv1.SubmitJobRequest{
_, err := f.srv.SubmitJob(testTenantCtx(), &controllerv1.SubmitJobRequest{
Scoring: &controllerv1.ScoringParams{Reference: "/r.yuv", Distorted: "/d.yuv"},
})
if err != nil {
Expand Down Expand Up @@ -496,9 +513,13 @@ func TestPullWork_InvalidSessionRejected(t *testing.T) {

func TestReportResult_FinalSuccess(t *testing.T) {
f := newGRPCFixture(t)
sub, _ := f.srv.SubmitJob(context.Background(), &controllerv1.SubmitJobRequest{
ctx := testTenantCtx()
sub, err := f.srv.SubmitJob(ctx, &controllerv1.SubmitJobRequest{
Scoring: &controllerv1.ScoringParams{Reference: "/r.yuv", Distorted: "/d.yuv"},
})
if err != nil {
t.Fatalf("SubmitJob: %v", err)
}
reg, _ := f.srv.RegisterNode(context.Background(), &controllerv1.RegisterNodeRequest{
Name: "rr-node",
Capability: &controllerv1.NodeCapability{Backends: []string{"cpu"}, Concurrency: 1},
Expand All @@ -524,17 +545,21 @@ func TestReportResult_FinalSuccess(t *testing.T) {
if !resp.GetOk() {
t.Error("ok: got false, want true")
}
job, _ := f.srv.GetJob(context.Background(), &controllerv1.GetJobRequest{JobId: sub.GetJobId()})
job, _ := f.srv.GetJob(ctx, &controllerv1.GetJobRequest{JobId: sub.GetJobId()})
if job.GetStatus() != controllerv1.JobStatus_COMPLETED {
t.Errorf("status: got %v, want COMPLETED", job.GetStatus())
}
}

func TestReportResult_FinalWithError(t *testing.T) {
f := newGRPCFixture(t)
sub, _ := f.srv.SubmitJob(context.Background(), &controllerv1.SubmitJobRequest{
ctx := testTenantCtx()
sub, err := f.srv.SubmitJob(ctx, &controllerv1.SubmitJobRequest{
Scoring: &controllerv1.ScoringParams{Reference: "/r.yuv", Distorted: "/d.yuv"},
})
if err != nil {
t.Fatalf("SubmitJob: %v", err)
}
reg, _ := f.srv.RegisterNode(context.Background(), &controllerv1.RegisterNodeRequest{
Name: "rr-node",
Capability: &controllerv1.NodeCapability{Backends: []string{"cpu"}, Concurrency: 1},
Expand All @@ -554,7 +579,7 @@ func TestReportResult_FinalWithError(t *testing.T) {
}); err != nil {
t.Fatalf("ReportResult: %v", err)
}
job, _ := f.srv.GetJob(context.Background(), &controllerv1.GetJobRequest{JobId: sub.GetJobId()})
job, _ := f.srv.GetJob(ctx, &controllerv1.GetJobRequest{JobId: sub.GetJobId()})
if job.GetStatus() != controllerv1.JobStatus_FAILED {
t.Errorf("status: got %v, want FAILED", job.GetStatus())
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/vmafx-controller/http_cancel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ func TestScoreHandler_ClientDisconnectKillsSubprocess(t *testing.T) {
reg := prometheus.NewRegistry()
metrics := observability.NewMetrics(reg)
log := observability.NewLogger("ERROR")
hs := newHTTPServer(scorer, metrics, reg, log)
hs := newHTTPServer(scorer, metrics, reg, nil, log)

mux := http.NewServeMux()
hs.routes(mux)
Expand Down
2 changes: 1 addition & 1 deletion cmd/vmafx-controller/main_extra_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ exit 1
reg := prometheus.NewRegistry()
metrics := observability.NewMetrics(reg)
log := observability.NewLogger("ERROR")
hs := newHTTPServer(scorer, metrics, reg, log)
hs := newHTTPServer(scorer, metrics, reg, nil, log)

mux := http.NewServeMux()
hs.routes(mux)
Expand Down
4 changes: 2 additions & 2 deletions cmd/vmafx-controller/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ func newTestHTTPServer(t *testing.T) (*httpServer, *prometheus.Registry) {
reg := prometheus.NewRegistry()
metrics := observability.NewMetrics(reg)
log := observability.NewLogger("ERROR") // suppress noise in tests
return newHTTPServer(scorer, metrics, reg, log), reg
return newHTTPServer(scorer, metrics, reg, nil, log), reg
}

// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -146,7 +146,7 @@ func TestReadyEndpointNotReady(t *testing.T) {
reg := prometheus.NewRegistry()
metrics := observability.NewMetrics(reg)
log := observability.NewLogger("ERROR")
hs := newHTTPServer(nil, metrics, reg, log) // nil scorer → not ready
hs := newHTTPServer(nil, metrics, reg, nil, log) // nil scorer → not ready
mux := http.NewServeMux()
hs.routes(mux)
ts := httptest.NewServer(mux)
Expand Down
Loading
Loading