CI / Nginx config check (push) Successful in 13s
CI / Env docs check (push) Successful in 15s
CI / Docker compose check (push) Successful in 15s
CI / Frontend major deps (push) Failing after 24s
CI / Frontend deps check (push) Successful in 30s
CI / Secrets scan (push) Successful in 38s
CI / Go build (push) Successful in 39s
CI / Frontend build (push) Successful in 1m3s
CI / Knip (push) Successful in 45s
CI / Go vet (prod) (push) Failing after 1m42s
CI / Frontend a11y check (push) Successful in 2m34s
CI / Go vet (dev) (push) Successful in 2m29s
CI / Staticcheck (prod) (push) Failing after 2m38s
CI / go mod tidy (push) Successful in 1m3s
CI / Staticcheck (dev) (push) Successful in 2m55s
CI / Frontend QC (audit) (push) Successful in 51s
CI / golangci-lint (push) Successful in 3m22s
CI / Go vulnerabilities (push) Successful in 1m26s
CI / Frontend QC (typecheck) (push) Successful in 2m18s
CI / Security scan (prod) (push) Successful in 4m18s
CI / Security scan (dev) (push) Successful in 4m40s
CI / Tests (prod) (push) Has been skipped
CI / Tests (dev) (push) Has been skipped
CI / Race (prod) (push) Has been skipped
CI / Race (dev) (push) Has been skipped
CI / Frontend QC (lint) (push) Successful in 2m18s
CI / Svelte strict check (push) Successful in 43s
New test files cover previously untested paths across DAV, validators, S3, Square, mw, bookings, user, and payments packages. Includes mock fix: HoldCheckouts flag on MockClient allows tests to pause auto-complete goroutine for testing PENDING checkout states. Coverage: 50.4% → 65.0% (+14.6pp)
496 lines
17 KiB
Go
496 lines
17 KiB
Go
//go:build test && dev
|
|
|
|
package bookings
|
|
|
|
// Package bookings contains targeted coverage improvements for under-tested
|
|
// booking handlers and helpers.
|
|
//
|
|
// Targets:
|
|
// - AdminCreateBookingForUserHandler: override service not in booking, custom
|
|
// override not in booking
|
|
// - AdminRejectEditRequestHandler: invalid request ID edge case
|
|
// - GetOverlappingBookingsHandler: no overlaps, non-existent booking, invalid ID
|
|
// - calculateServiceDurationWithOverrides: direct unit tests
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
|
|
"crussell/db"
|
|
"crussell/mw"
|
|
"crussell/testutils"
|
|
"crussell/testutils/fixtures"
|
|
)
|
|
|
|
// =============================================================================
|
|
// AdminCreateBookingForUserHandler — additional coverage
|
|
// =============================================================================
|
|
|
|
// TestAdminCreateBookingForUserHandler_OverrideServiceNotInBooking verifies
|
|
// that providing an override for a regular service that was NOT added to the
|
|
// booking returns 400 Bad Request.
|
|
func TestAdminCreateBookingForUserHandler_OverrideServiceNotInBooking(t *testing.T) {
|
|
ctx, tx := testutils.SetupTestTx(t)
|
|
|
|
adminID, err := fixtures.CreateTestAdminUser(tx)
|
|
if err != nil {
|
|
t.Fatalf("failed to create admin: %v", err)
|
|
}
|
|
userID, err := fixtures.CreateTestUser(tx)
|
|
if err != nil {
|
|
t.Fatalf("failed to create user: %v", err)
|
|
}
|
|
_, err = tx.Exec(ctx, "UPDATE users SET deposits_required = 0 WHERE id = $1", userID)
|
|
if err != nil {
|
|
t.Fatalf("failed to set deposits_required: %v", err)
|
|
}
|
|
serviceA, err := fixtures.CreateTestService(tx)
|
|
if err != nil {
|
|
t.Fatalf("failed to create service A: %v", err)
|
|
}
|
|
serviceB, err := fixtures.CreateTestService(tx)
|
|
if err != nil {
|
|
t.Fatalf("failed to create service B: %v", err)
|
|
}
|
|
|
|
overridePrice := 75.0
|
|
body := AdminCreateBookingForUserRequest{
|
|
UserID: userID,
|
|
StartTime: weekdayTime(time.Wednesday, 10),
|
|
ServiceIDs: []string{serviceA},
|
|
// Override references serviceB which is NOT in ServiceIDs
|
|
ServiceOverrides: []ServiceOverride{
|
|
{ServiceID: serviceB, OverridePrice: &overridePrice},
|
|
},
|
|
}
|
|
|
|
w := serveChiHandler(AdminCreateBookingForUserHandler, "POST", "/", "/", body,
|
|
func(baseCtx context.Context) context.Context {
|
|
baseCtx = context.WithValue(baseCtx, mw.UserRoleKey, "admin")
|
|
baseCtx = context.WithValue(baseCtx, mw.UserIDKey, adminID)
|
|
return db.ContextWithTx(baseCtx, db.TxFromContext(ctx))
|
|
})
|
|
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("expected 400 for override not in booking, got %d. body: %s", w.Code, w.Body.String())
|
|
}
|
|
}
|
|
|
|
// TestAdminCreateBookingForUserHandler_CustomOverrideNotInBooking verifies that
|
|
// providing an override for a custom service not in the booking returns 400.
|
|
func TestAdminCreateBookingForUserHandler_CustomOverrideNotInBooking(t *testing.T) {
|
|
ctx, tx := testutils.SetupTestTx(t)
|
|
|
|
adminID, err := fixtures.CreateTestAdminUser(tx)
|
|
if err != nil {
|
|
t.Fatalf("failed to create admin: %v", err)
|
|
}
|
|
userID, err := fixtures.CreateTestUser(tx)
|
|
if err != nil {
|
|
t.Fatalf("failed to create user: %v", err)
|
|
}
|
|
_, err = tx.Exec(ctx, "UPDATE users SET deposits_required = 0 WHERE id = $1", userID)
|
|
if err != nil {
|
|
t.Fatalf("failed to set deposits_required: %v", err)
|
|
}
|
|
|
|
// Insert a custom service
|
|
var customSvcID string
|
|
err = tx.QueryRow(ctx, `
|
|
INSERT INTO custom_services (name, description, price, duration_minutes)
|
|
VALUES ($1, $2, $3, $4)
|
|
RETURNING id
|
|
`, "Test Custom", "Custom service", 60.00, 45).Scan(&customSvcID)
|
|
if err != nil {
|
|
t.Fatalf("failed to create custom service: %v", err)
|
|
}
|
|
|
|
// Another custom service that is NOT included in the booking
|
|
var otherCustomSvcID string
|
|
err = tx.QueryRow(ctx, `
|
|
INSERT INTO custom_services (name, description, price, duration_minutes)
|
|
VALUES ($1, $2, $3, $4)
|
|
RETURNING id
|
|
`, "Other Custom", "Not in booking", 40.00, 30).Scan(&otherCustomSvcID)
|
|
if err != nil {
|
|
t.Fatalf("failed to create other custom service: %v", err)
|
|
}
|
|
|
|
overridePrice := 50.0
|
|
body := AdminCreateBookingForUserRequest{
|
|
UserID: userID,
|
|
StartTime: weekdayTime(time.Wednesday, 10),
|
|
CustomServiceIDs: []string{customSvcID},
|
|
// Override references otherCustomSvcID which is NOT in CustomServiceIDs
|
|
CustomOverrides: []ServiceOverride{
|
|
{ServiceID: otherCustomSvcID, OverridePrice: &overridePrice},
|
|
},
|
|
}
|
|
|
|
w := serveChiHandler(AdminCreateBookingForUserHandler, "POST", "/", "/", body,
|
|
func(baseCtx context.Context) context.Context {
|
|
baseCtx = context.WithValue(baseCtx, mw.UserRoleKey, "admin")
|
|
baseCtx = context.WithValue(baseCtx, mw.UserIDKey, adminID)
|
|
return db.ContextWithTx(baseCtx, db.TxFromContext(ctx))
|
|
})
|
|
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("expected 400 for custom override not in booking, got %d. body: %s", w.Code, w.Body.String())
|
|
}
|
|
}
|
|
|
|
// =============================================================================
|
|
// AdminRejectEditRequestHandler — additional coverage
|
|
// =============================================================================
|
|
|
|
// TestAdminRejectEditRequestHandler_InvalidRequestID verifies that rejecting
|
|
// an edit request with an invalid (non-hex) request ID returns 404.
|
|
func TestAdminRejectEditRequestHandler_InvalidRequestID(t *testing.T) {
|
|
ctx, tx := testutils.SetupTestTx(t)
|
|
|
|
adminID, err := fixtures.CreateTestAdminUser(tx)
|
|
if err != nil {
|
|
t.Fatalf("failed to create admin: %v", err)
|
|
}
|
|
|
|
// Route: /api/admin/bookings/{id}/edit-requests/{request_id}/deny
|
|
// Use a booking ID that passes IsValidID but a request_id that does not
|
|
bookingID := "aaaaaaaaaaaa" // valid 12-char hex
|
|
invalidRequestID := "not-a-valid-id"
|
|
|
|
w := serveChiHandler(AdminRejectEditRequestHandler, "POST",
|
|
"/api/admin/bookings/"+bookingID+"/edit-requests/"+invalidRequestID+"/deny",
|
|
"/api/admin/bookings/{id}/edit-requests/{request_id}/deny", nil,
|
|
func(baseCtx context.Context) context.Context {
|
|
baseCtx = context.WithValue(baseCtx, mw.UserRoleKey, "admin")
|
|
baseCtx = context.WithValue(baseCtx, mw.UserIDKey, adminID)
|
|
return db.ContextWithTx(baseCtx, db.TxFromContext(ctx))
|
|
})
|
|
|
|
if w.Code != http.StatusNotFound {
|
|
t.Errorf("expected 404 for invalid request ID, got %d. body: %s", w.Code, w.Body.String())
|
|
}
|
|
}
|
|
|
|
// =============================================================================
|
|
// GetOverlappingBookingsHandler — additional coverage
|
|
// =============================================================================
|
|
|
|
// TestAdminGetOverlappingBookingsHandler_NoOverlaps verifies that when a booking
|
|
// has no overlapping bookings, an empty list is returned.
|
|
func TestAdminGetOverlappingBookingsHandler_NoOverlaps(t *testing.T) {
|
|
ctx, tx := testutils.SetupTestTx(t)
|
|
|
|
adminID, err := fixtures.CreateTestAdminUser(tx)
|
|
if err != nil {
|
|
t.Fatalf("failed to create admin: %v", err)
|
|
}
|
|
userID, err := fixtures.CreateTestUser(tx)
|
|
if err != nil {
|
|
t.Fatalf("failed to create user: %v", err)
|
|
}
|
|
serviceID, err := fixtures.CreateTestService(tx)
|
|
if err != nil {
|
|
t.Fatalf("failed to create service: %v", err)
|
|
}
|
|
|
|
// Create a single confirmed booking with no other bookings near it
|
|
baseTime := weekdayTime(time.Wednesday, 10)
|
|
bookingID, err := fixtures.CreateTestBookingAtTime(tx, userID, serviceID, baseTime)
|
|
if err != nil {
|
|
t.Fatalf("failed to create booking: %v", err)
|
|
}
|
|
_, err = tx.Exec(ctx, "UPDATE bookings SET status = 'confirmed' WHERE id = $1", bookingID)
|
|
if err != nil {
|
|
t.Fatalf("failed to confirm booking: %v", err)
|
|
}
|
|
|
|
// Query overlapping for the booking — there are no other bookings
|
|
w := serveChiHandler(GetOverlappingBookingsHandler, "GET",
|
|
"/"+bookingID+"/overlapping", "/{id}/overlapping", nil,
|
|
adminCtx(adminID, ctx), ctx)
|
|
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("expected 200 OK, got %d. body: %s", w.Code, w.Body.String())
|
|
}
|
|
|
|
var resp OverlappingBookingsResponse
|
|
if err := parseResponseBody(w, &resp); err != nil {
|
|
t.Fatalf("failed to parse response: %v", err)
|
|
}
|
|
if len(resp.Bookings) != 0 {
|
|
t.Errorf("expected 0 overlapping bookings, got %d", len(resp.Bookings))
|
|
}
|
|
}
|
|
|
|
// TestAdminGetOverlappingBookingsHandler_NonExistentBooking verifies that
|
|
// querying overlapping bookings for a non-existent booking returns 404.
|
|
func TestAdminGetOverlappingBookingsHandler_NonExistentBooking(t *testing.T) {
|
|
ctx, tx := testutils.SetupTestTx(t)
|
|
|
|
adminID, err := fixtures.CreateTestAdminUser(tx)
|
|
if err != nil {
|
|
t.Fatalf("failed to create admin: %v", err)
|
|
}
|
|
|
|
// Use a valid 12-char hex ID that does not exist in the DB
|
|
nonExistentID := "aaaaaaaaaaaa"
|
|
|
|
w := serveChiHandler(GetOverlappingBookingsHandler, "GET",
|
|
"/"+nonExistentID+"/overlapping", "/{id}/overlapping", nil,
|
|
adminCtx(adminID, ctx), ctx)
|
|
|
|
if w.Code != http.StatusNotFound {
|
|
t.Errorf("expected 404 for non-existent booking, got %d. body: %s", w.Code, w.Body.String())
|
|
}
|
|
}
|
|
|
|
// TestAdminGetOverlappingBookingsHandler_InvalidBookingID verifies that
|
|
// querying overlapping bookings with an invalid (non-hex) booking ID returns 404.
|
|
func TestAdminGetOverlappingBookingsHandler_InvalidBookingID(t *testing.T) {
|
|
ctx, tx := testutils.SetupTestTx(t)
|
|
|
|
adminID, err := fixtures.CreateTestAdminUser(tx)
|
|
if err != nil {
|
|
t.Fatalf("failed to create admin: %v", err)
|
|
}
|
|
|
|
// Use an invalid ID that fails IsValidID
|
|
invalidID := "not-valid"
|
|
|
|
w := serveChiHandler(GetOverlappingBookingsHandler, "GET",
|
|
"/"+invalidID+"/overlapping", "/{id}/overlapping", nil,
|
|
adminCtx(adminID, ctx), ctx)
|
|
|
|
if w.Code != http.StatusNotFound {
|
|
t.Errorf("expected 404 for invalid booking ID, got %d. body: %s", w.Code, w.Body.String())
|
|
}
|
|
}
|
|
|
|
// =============================================================================
|
|
// calculateServiceDurationWithOverrides — direct unit tests
|
|
// =============================================================================
|
|
|
|
// TestCalculateServiceDurationWithOverrides_Normal verifies that the total
|
|
// duration is the sum of all service durations when no overrides are provided.
|
|
func TestCalculateServiceDurationWithOverrides_Normal(t *testing.T) {
|
|
ctx, tx := testutils.SetupTestTx(t)
|
|
|
|
svc1, err := fixtures.CreateTestServiceWithDuration(tx, 60)
|
|
if err != nil {
|
|
t.Fatalf("failed to create service 1: %v", err)
|
|
}
|
|
svc2, err := fixtures.CreateTestServiceWithDuration(tx, 30)
|
|
if err != nil {
|
|
t.Fatalf("failed to create service 2: %v", err)
|
|
}
|
|
|
|
// Create a context with the tx so db.Conn routes through it
|
|
txCtx := db.ContextWithTx(context.Background(), db.TxFromContext(ctx))
|
|
|
|
dur, err := calculateServiceDurationWithOverrides(txCtx, []string{svc1, svc2}, nil)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if dur != 90 {
|
|
t.Errorf("expected duration 90 (60+30), got %d", dur)
|
|
}
|
|
}
|
|
|
|
// TestCalculateServiceDurationWithOverrides_WithOverride verifies that when an
|
|
// override duration is provided for a regular service, the override is used
|
|
// instead of the service's default duration.
|
|
func TestCalculateServiceDurationWithOverrides_WithOverride(t *testing.T) {
|
|
ctx, tx := testutils.SetupTestTx(t)
|
|
|
|
svc1, err := fixtures.CreateTestServiceWithDuration(tx, 60)
|
|
if err != nil {
|
|
t.Fatalf("failed to create service 1: %v", err)
|
|
}
|
|
svc2, err := fixtures.CreateTestServiceWithDuration(tx, 30)
|
|
if err != nil {
|
|
t.Fatalf("failed to create service 2: %v", err)
|
|
}
|
|
|
|
overrideDur := 45
|
|
overrides := []ServiceOverrideRequest{
|
|
{ServiceID: svc1, OverrideDurationMinutes: &overrideDur},
|
|
}
|
|
|
|
txCtx := db.ContextWithTx(context.Background(), db.TxFromContext(ctx))
|
|
|
|
dur, err := calculateServiceDurationWithOverrides(txCtx, []string{svc1, svc2}, overrides)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
// svc1 overridden to 45, svc2 stays at 30 => total 75
|
|
if dur != 75 {
|
|
t.Errorf("expected duration 75 (45+30), got %d", dur)
|
|
}
|
|
}
|
|
|
|
// TestCalculateServiceDurationWithOverrides_CustomService verifies that custom
|
|
// services are included in the duration calculation and overrides work for them.
|
|
func TestCalculateServiceDurationWithOverrides_CustomService(t *testing.T) {
|
|
ctx, tx := testutils.SetupTestTx(t)
|
|
|
|
svcID, err := fixtures.CreateTestServiceWithDuration(tx, 60)
|
|
if err != nil {
|
|
t.Fatalf("failed to create service: %v", err)
|
|
}
|
|
|
|
// Create a custom service directly
|
|
var customSvcID string
|
|
err = tx.QueryRow(ctx, `
|
|
INSERT INTO custom_services (name, description, price, duration_minutes)
|
|
VALUES ($1, $2, $3, $4)
|
|
RETURNING id
|
|
`, "Test Custom Svc", "Custom for duration test", 50.00, 45).Scan(&customSvcID)
|
|
if err != nil {
|
|
t.Fatalf("failed to create custom service: %v", err)
|
|
}
|
|
|
|
txCtx := db.ContextWithTx(context.Background(), db.TxFromContext(ctx))
|
|
|
|
dur, err := calculateServiceDurationWithOverrides(txCtx, []string{svcID, customSvcID}, nil)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
// svc: 60, custom: 45 => total 105
|
|
if dur != 105 {
|
|
t.Errorf("expected duration 105 (60+45), got %d", dur)
|
|
}
|
|
|
|
// Now test with override on the custom service
|
|
overrideDur := 30
|
|
overrides := []ServiceOverrideRequest{
|
|
{ServiceID: customSvcID, OverrideDurationMinutes: &overrideDur},
|
|
}
|
|
|
|
dur, err = calculateServiceDurationWithOverrides(txCtx, []string{svcID, customSvcID}, overrides)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
// svc: 60, custom overridden to 30 => total 90
|
|
if dur != 90 {
|
|
t.Errorf("expected duration 90 (60+30), got %d", dur)
|
|
}
|
|
}
|
|
|
|
// TestCalculateServiceDurationWithOverrides_MixedOverrides verifies that when
|
|
// some services have overrides and others don't, the correct total is computed.
|
|
func TestCalculateServiceDurationWithOverrides_MixedOverrides(t *testing.T) {
|
|
ctx, tx := testutils.SetupTestTx(t)
|
|
|
|
svc1, err := fixtures.CreateTestServiceWithDuration(tx, 60)
|
|
if err != nil {
|
|
t.Fatalf("failed to create service 1: %v", err)
|
|
}
|
|
svc2, err := fixtures.CreateTestServiceWithDuration(tx, 30)
|
|
if err != nil {
|
|
t.Fatalf("failed to create service 2: %v", err)
|
|
}
|
|
svc3, err := fixtures.CreateTestServiceWithDuration(tx, 90)
|
|
if err != nil {
|
|
t.Fatalf("failed to create service 3: %v", err)
|
|
}
|
|
|
|
// Override svc1 to 45 and svc3 to 60; svc2 stays at 30
|
|
override1 := 45
|
|
override3 := 60
|
|
overrides := []ServiceOverrideRequest{
|
|
{ServiceID: svc1, OverrideDurationMinutes: &override1},
|
|
{ServiceID: svc3, OverrideDurationMinutes: &override3},
|
|
}
|
|
|
|
txCtx := db.ContextWithTx(context.Background(), db.TxFromContext(ctx))
|
|
|
|
dur, err := calculateServiceDurationWithOverrides(txCtx, []string{svc1, svc2, svc3}, overrides)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
// svc1: 45, svc2: 30, svc3: 60 => total 135
|
|
if dur != 135 {
|
|
t.Errorf("expected duration 135 (45+30+60), got %d", dur)
|
|
}
|
|
}
|
|
|
|
// TestCalculateServiceDurationWithOverrides_SingleService verifies that a single
|
|
// service with no overrides returns its own duration.
|
|
func TestCalculateServiceDurationWithOverrides_SingleService(t *testing.T) {
|
|
ctx, tx := testutils.SetupTestTx(t)
|
|
|
|
svcID, err := fixtures.CreateTestServiceWithDuration(tx, 45)
|
|
if err != nil {
|
|
t.Fatalf("failed to create service: %v", err)
|
|
}
|
|
|
|
txCtx := db.ContextWithTx(context.Background(), db.TxFromContext(ctx))
|
|
|
|
dur, err := calculateServiceDurationWithOverrides(txCtx, []string{svcID}, nil)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if dur != 45 {
|
|
t.Errorf("expected duration 45, got %d", dur)
|
|
}
|
|
}
|
|
|
|
// TestCalculateServiceDurationWithOverrides_EmptyServices verifies that an
|
|
// empty service list returns 0 duration.
|
|
func TestCalculateServiceDurationWithOverrides_EmptyServices(t *testing.T) {
|
|
ctx, _ := testutils.SetupTestTx(t)
|
|
|
|
txCtx := db.ContextWithTx(context.Background(), db.TxFromContext(ctx))
|
|
|
|
dur, err := calculateServiceDurationWithOverrides(txCtx, []string{}, nil)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if dur != 0 {
|
|
t.Errorf("expected duration 0 for empty services, got %d", dur)
|
|
}
|
|
}
|
|
|
|
// TestCalculateServiceDurationWithOverrides_NilOverrides verifies that nil
|
|
// overrides (when len(overrides)==0) goes through the sum path correctly.
|
|
func TestCalculateServiceDurationWithOverrides_NilOverrides(t *testing.T) {
|
|
ctx, tx := testutils.SetupTestTx(t)
|
|
|
|
svcID, err := fixtures.CreateTestServiceWithDuration(tx, 60)
|
|
if err != nil {
|
|
t.Fatalf("failed to create service: %v", err)
|
|
}
|
|
|
|
txCtx := db.ContextWithTx(context.Background(), db.TxFromContext(ctx))
|
|
|
|
// Pass nil overrides explicitly
|
|
dur, err := calculateServiceDurationWithOverrides(txCtx, []string{svcID}, nil)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if dur != 60 {
|
|
t.Errorf("expected duration 60, got %d", dur)
|
|
}
|
|
}
|
|
|
|
// TestCalculateServiceDurationWithOverrides_NonExistentService verifies that
|
|
// passing a non-existent service ID returns 0 duration (the SUM will be over
|
|
// empty rows).
|
|
func TestCalculateServiceDurationWithOverrides_NonExistentService(t *testing.T) {
|
|
ctx, _ := testutils.SetupTestTx(t)
|
|
|
|
txCtx := db.ContextWithTx(context.Background(), db.TxFromContext(ctx))
|
|
|
|
// A valid hex ID that doesn't exist as a service or custom service
|
|
dur, err := calculateServiceDurationWithOverrides(txCtx, []string{"aaaaaaaaaaaa"}, nil)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if dur != 0 {
|
|
t.Errorf("expected duration 0 for non-existent service, got %d", dur)
|
|
}
|
|
}
|