Skip to main content

Testing

Version: 0.1.0

DecisionBox has 350+ tests across unit and integration suites. This guide covers running tests and writing new ones.

Running Tests

All Tests

make test          # All Go + UI tests
make test-go # All Go unit tests only
make lint # All linters (golangci-lint + ESLint)

By Category

CommandWhat It TestsNeeds
make test-goAll Go unit tests across all modulesNothing
make test-uiDashboard unit tests (Jest)Node.js
make lint-goGo linting via golangci-lintgolangci-lint
make lint-uiDashboard linting via ESLintNode.js
make test-integrationMongoDB + API integration testsDocker
make test-k8sK8s runner tests (K3s testcontainer)Docker
make test-secretsSecret provider integration testsDocker
make test-ollamaOllama LLM integration tests (slow)Docker
make test-llmLLM provider integration testsAPI keys

CI Pipeline

The CI workflow runs automatically on every PR and push to main:

JobWhat It DoesTrigger
Go BuildCompiles API + Agent binariesGo files changed
Go TestAll Go unit testsGo files changed
Go Lintgolangci-lint on all modulesGo files changed
Dashboard BuildNext.js buildDashboard files changed
Dashboard Test & LintJest + ESLintDashboard files changed
License CheckSBOM generation (Syft) + license policy validation (Grant)Go or Dashboard files changed
Integration TestsMongoDB integration testsPush to main, or PR with run-integration-tests label

To trigger integration tests on a PR, add the run-integration-tests label.

LLM Integration Tests

These test against real LLM APIs. They skip gracefully when credentials are not set:

export INTEGRATION_TEST_ANTHROPIC_API_KEY=sk-ant-...
export INTEGRATION_TEST_OPENAI_API_KEY=sk-...
export INTEGRATION_TEST_VERTEX_PROJECT_ID=my-gcp-project
export INTEGRATION_TEST_BEDROCK_REGION=us-east-1

make test-llm

Test Structure

Go Tests

Tests live alongside the code they test:

services/agent/
├── internal/
│ ├── models/
│ │ ├── discovery.go # Code
│ │ └── discovery_test.go # Unit tests
│ ├── discovery/
│ │ ├── orchestrator.go
│ │ ├── selective_test.go # Unit tests
│ │ └── integration_test.go # Integration tests (//go:build integration)

Integration Tests

Integration tests use build tags:

//go:build integration

package discovery

They're excluded from go test ./... and only run with -tags=integration.

Testcontainers

Integration tests use testcontainers-go to spin up dependencies:

DependencyTestcontainerUsed By
MongoDBtestcontainers-go/modules/mongodbAgent + API integration tests
K3stestcontainers-go/modules/k3sK8s runner tests
Ollamatestcontainers-go/modules/ollamaOllama LLM tests

Dashboard Tests

cd ui/dashboard
npm test # Run once
npm run test:watch # Watch mode
npm run test:coverage # Coverage report

Uses Jest + React Testing Library. Test files: *.test.ts or *.test.tsx.

Writing Tests

Unit Test Example

// providers/llm/myprovider/provider_test.go
package myprovider

import (
"testing"

gollm "github.com/decisionbox-io/decisionbox/libs/go-common/llm"
)

func TestRegistered(t *testing.T) {
_, ok := gollm.GetProviderMeta("myprovider")
if !ok {
t.Fatal("myprovider not registered")
}
}

func TestFactoryMissingKey(t *testing.T) {
_, err := gollm.NewProvider("myprovider", gollm.ProviderConfig{})
if err == nil {
t.Fatal("should error without API key")
}
}

Integration Test Example

//go:build integration

package myprovider

import (
"context"
"os"
"testing"
"time"

gollm "github.com/decisionbox-io/decisionbox/libs/go-common/llm"
)

func TestIntegration_BasicChat(t *testing.T) {
apiKey := os.Getenv("INTEGRATION_TEST_MYPROVIDER_API_KEY")
if apiKey == "" {
t.Skip("INTEGRATION_TEST_MYPROVIDER_API_KEY not set")
}

provider, _ := gollm.NewProvider("myprovider", gollm.ProviderConfig{
"api_key": apiKey,
})

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

resp, err := provider.Chat(ctx, gollm.ChatRequest{
Messages: []gollm.Message{{Role: "user", Content: "Say hello."}},
MaxTokens: 10,
})
if err != nil {
t.Fatalf("Chat error: %v", err)
}
if resp.Content == "" {
t.Error("empty response")
}
}

Test Quality Standards

  • One thing per test — Each test verifies one behavior
  • Descriptive namesTestRecommendationJSON_WithRelatedInsights, not TestFunc1
  • No external dependencies — Unit tests don't need Docker, APIs, or credentials
  • Skip gracefully — Integration tests use t.Skip() when credentials are missing
  • Fast — Unit tests should complete in < 100ms
  • Deterministic — No random failures

What to Test

When adding a new feature:

ChangeRequired Tests
New providerRegistration, config validation, factory errors, interface compliance
New API endpointHandler unit test, integration test with MongoDB
New model fieldJSON marshal/unmarshal round-trip
New agent logicUnit test with mock dependencies
UI componentJest test for rendering and behavior

Test Coverage

Run coverage for a specific package:

cd services/agent/internal/models
go test -cover ./...

# HTML report
go test -coverprofile=cover.out ./...
go tool cover -html=cover.out

Next Steps