Get started in less than 5 minInstall now

Testing

This guide covers the knodex test architecture, how to run tests, and how to write new ones.


Test Tiers

knodex uses a tiered testing approach:

TierSpeedInfrastructureScopeWhen Run
UnitFast (seconds)NoneIndividual functionsEvery go test, npm test
IntegrationMedium (minutes)Mocks/stubsComponent interactionsPR builds
E2ESlow (5-15 min)Full clusterUser workflowsExplicit trigger

Why E2E Tests Skip by Default

Server E2E tests use build tags:

//go:build e2e

This is intentional. E2E tests require a running Kind cluster with deployed application, test data, and mock OIDC server. Running go test ./... without this infrastructure would fail with confusing errors.


Running Tests

Unit Tests (Daily Development)

# All unit tests
make test
 
# Single server test
cd server && go test -v -run TestHealthz ./internal/api/handlers/
 
# Single web test (filter by test name)
cd web && npx vitest run -t "should render"

E2E Tests (Before PR/Merge)

# One-time: Create cluster with prerequisites
make cluster-up
 
# Run E2E tests (auto-deploys if needed)
make e2e
 
# Or full QA cycle: deploy + all tests
make qa

Specific E2E Tests

# Web E2E (Playwright)
cd web && npx playwright test auth_login_test.spec.ts
 
# Server E2E (Go, requires cluster)
cd server && go test -tags=e2e -v -run TestRBACPermissions ./test/e2e/...

Test Locations

CategoryLocationCommand
Server unit testsserver/internal/*/cd server && go test ./...
Server E2E testsserver/test/e2e/cd server && go test -tags=e2e ./test/e2e/...
Web unit testsweb/src/**/*.test.tscd web && npm test
Web E2E testsweb/test/e2e/cd web && npx playwright test

E2E Test Frameworks

knodex uses two E2E frameworks:

FrameworkLocationPurpose
Playwrightweb/test/e2e/UI tests, browser interactions, user workflows
Go E2Eserver/test/e2e/API tests, Kubernetes resource tests

Both run against a real Kind cluster with actual CRDs and test data.

Prerequisites

  • Go 1.24+, Node.js 20+, Docker, Kind, kubectl
# Install Playwright browsers
cd web && npm install && npx playwright install chromium

Configuring Test Secrets (Optional)

Repository connection tests need a GitHub token. Most contributors can skip this.

cp .env.example .env
# Edit .env and add your GitHub Personal Access Token (repo scope)
# The token is picked up automatically by `make qa`

The .env file is in .gitignore — never commit real tokens.

Environment Variables

VariableDefaultDescription
E2E_BASE_URLhttp://localhost:3000Web URL for Playwright tests
E2E_API_URLhttp://localhost:8080Server API URL
E2E_JWT_SECRETtest-secret-key-minimum-32-characters-requiredJWT signing secret (must match server)
E2E_TIMEOUT60000Test timeout in milliseconds
CIunsetEnables CI mode (stricter failure handling)

Authentication in Tests

Frontend E2E tests use JWT token injection to bypass the login flow.

Using Test Fixtures

import { test } from '../fixture';
import { TestUserRole } from '../fixture/auth-helper';
 
test.use({ authenticateAs: TestUserRole.GLOBAL_ADMIN });
 
test('admin can view all RGDs', async ({ page }) => {
  await page.goto('/catalog');
  // Test assertions...
});

Manual Authentication

import { test } from '@playwright/test';
import { setupAuthAndNavigate, TestUserRole } from '../fixture';
 
test('viewer cannot deploy', async ({ page }) => {
  await setupAuthAndNavigate(page, TestUserRole.ORG_VIEWER, '/catalog');
  await expect(page.getByRole('button', { name: 'Deploy' })).not.toBeVisible();
});

Available Test Users

RoleDescriptionProjects
GLOBAL_ADMINFull platform accessAll projects
ORG_ADMINProject adminproj-alpha-team
ORG_DEVELOPERProject developerproj-alpha-team
ORG_VIEWERRead-only accessproj-alpha-team
OIDC_WILDCARD_USEROIDC user with group-based accessproj-azuread-staging

Test Isolation

Frontend: Each test file runs in isolation with a fresh browser context, clean localStorage, and new auth tokens. Tests can run in parallel.

Backend: Tests use Kubernetes namespaces for isolation with cleanup after each suite.


Writing New Tests

Frontend Test Template

// test/e2e/feature_name_test.spec.ts
import { test, expect } from '@playwright/test';
import { setupAuthAndNavigate, TestUserRole } from '../fixture';
 
test.describe('Feature Name', () => {
  test.beforeEach(async ({ page }) => {
    await setupAuthAndNavigate(page, TestUserRole.GLOBAL_ADMIN, '/target-page');
  });
 
  test('should do expected behavior', async ({ page }) => {
    await page.getByRole('button', { name: 'Action' }).click();
    await expect(page.getByText('Success')).toBeVisible();
  });
});

Backend Test Template

// test/e2e/feature_test.go
//go:build e2e
 
package e2e
 
import (
    "testing"
    "github.com/stretchr/testify/require"
)
 
func TestFeatureName(t *testing.T) {
    t.Run("should do expected behavior", func(t *testing.T) {
        client := getTestClient(t)
        result, err := client.DoSomething()
        require.NoError(t, err)
        require.Equal(t, expected, result)
    })
}

Naming Conventions

  • Frontend: {feature}_{subfeature}_test.spec.ts (e.g., catalog_list_test.spec.ts)
  • Backend: {feature}_test.go (e.g., rbac_test.go)

Debugging Failed Tests

# View Playwright HTML report
cd web && npx playwright show-report
 
# Run in debug mode (step through)
cd web && npx playwright test --debug auth_login_test.spec.ts
 
# View trace file
npx playwright show-trace test-results/auth_login_test-Authentication--chromium/trace.zip

Test artifacts are saved to web/test-results/ (screenshots, videos, traces).


Common Issues

SymptomCauseSolution
Tests stuck on login pageToken injection failedCheck E2E_JWT_SECRET matches server config
browserType.launch: Executable doesn't existPlaywright not installedcd web && npx playwright install chromium
connect ECONNREFUSED 127.0.0.1:8080Server not runningmake qa or make dev
Tests exceed 60s timeoutSlow API / missing test dataCheck server logs, verify test data exists
OIDC tests skippedExpected behaviorOIDC tests require mock OIDC server (deployed by make qa)

CI/CD Pipeline

PR Opened
    ├── Server CI (go test, go vet)
    ├── Web CI (npm test, eslint)         ← Fast
    └── Docker Build (build, verify)
              ↓
        E2E Tests
        - Deploy to Kind cluster
        - Run Playwright tests              ← Slow
        - Run Go E2E tests
        - Collect artifacts
              ↓
        Merge Gate (all checks must pass)

Pass Rate Requirements

Test CategoryMinimum Pass Rate
Unit tests100%
Critical E2E (auth, RBAC)95%
Non-critical E2E85%

View CI Results

Check the "E2E Tests" workflow in GitHub Actions for test logs, Playwright HTML report (as artifact), and failure screenshots.


KRO Canary Testing

The KRO canary suite tests Knodex against KRO main to detect silent feature gaps and behavioral changes before KRO releases.

Purpose

KRO is pinned to a specific version in server/go.mod. When KRO's main branch introduces new features or changes CRD generation behavior, Knodex may not handle the new output. The canary tests catch these gaps early by:

  1. Deploying KRO from main to a Kind cluster
  2. Applying reference RGDs (deploy/test/reference-rgds/) that exercise specific KRO features
  3. Running Knodex schema extraction against each RGD
  4. Comparing API output against committed baseline snapshots (deploy/test/reference-rgds/expected/)

Weekly Schedule

The canary runs automatically every Monday at 06:00 UTC via .github/workflows/kro-canary.yml. When a mismatch is detected, a GitHub issue is auto-created with the kro-compatibility label.

Running Manually

# Trigger via GitHub Actions UI (Actions → KRO Canary Tests → Run workflow)
# Or via CLI:
gh workflow run kro-canary.yml
 
# Run with a specific KRO ref:
gh workflow run kro-canary.yml -f kro_ref=v0.10.0

Updating Baselines

When a schema change is intentional (Knodex has been updated to handle new KRO output):

# Option 1: Trigger via GitHub Actions with baseline update
gh workflow run kro-canary.yml -f update_baselines=true
 
# Option 2: Run locally against a cluster with KRO + reference RGDs applied
cd server && go test -tags=canary -run TestCanary -update ./test/canary/

Updated baselines should be committed and pushed.

Reference RGDs

Each reference RGD in deploy/test/reference-rgds/ tests a specific KRO feature:

FileFeatureKRO Min Version
basic-types.yamlString, integer, boolean with defaultsv0.8.0
conditional-resources.yamlincludeWhen with boolean conditionsv0.8.0
external-refs.yamlexternalRef with name/namespace pairsv0.8.0
nested-external-refs.yamlCross-RGD externalRef lookupsv0.9.0
advanced-section.yamlspec.advanced nested fieldsv0.8.0
cel-expressions.yamlCEL ternary, equality, and string conditionsv0.9.0

Interpreting Issues

When the canary creates a kro-compatibility issue:

  1. Review the diff — does KRO now produce new fields, changed types, or different structure?
  2. Check KRO changelog — was this an intentional change?
  3. Update Knodex — add handling for the new output in internal/kro/ packages
  4. Regenerate baselines — run with -update flag after Knodex is updated

Test Locations

ComponentLocation
Reference RGDsdeploy/test/reference-rgds/
Golden baselinesdeploy/test/reference-rgds/expected/
Go canary testsserver/test/canary/ (build tag: canary)
CI workflow.github/workflows/kro-canary.yml