Skip to content

Latest commit

 

History

History
233 lines (176 loc) · 9.77 KB

TESTING_CE_ENT.md

File metadata and controls

233 lines (176 loc) · 9.77 KB

Testing Strategy for CE/ENT Split

Overview

This document describes the testing strategy for handling Community Edition (CE) and Enterprise Edition (ENT) test suites.

Where enterprise tests of core packages live (read this first)

A test in this repository must never import the enterprise module (github.com/TykTechnologies/ai-studio-enterprise/v2), not even behind //go:build enterprise. go mod tidy in a program that embeds Studio reads the tests of every package it imports, under every build tag, so such a test makes every Community Edition consumer try to fetch the private repository. make enterprise-import-guard (CI) enforces this.

Enterprise tests of core packages, including the TestMain files below, therefore live in the enterprise repository under enterprise/_coretests/<core package dir>/<name>_entlink_test.go, with the package clause of the core package they test. make ent-link (scripts/enterprise-link-tests.sh) symlinks them into their core package directories, where *_entlink_test.go is gitignored; make init-enterprise, make test, make test-studio-* and the enterprise CI job run it. After that, GOWORK=$PWD/enterprise.work go test -tags enterprise ./api/... runs them. Edit them in place: the links point into the enterprise submodule, so commit them there.

The root go.mod does not require the enterprise module at all: a plugin author or host inherits the root's requirements, and a private module there makes their go list -m all (govulncheck, Dependabot, Renovate) fail. make sdk-consumer-check (CI) checks this. Enterprise builds and tests of the root module therefore resolve it through the workspace file enterprise.work (use . ./enterprise), set explicitly with GOWORK=$PWD/enterprise.work; the Makefile targets, goreleaser, the dev environment and CI do so. It is not called go.work because Go would then apply it to the microgateway and plugin modules below the root, which are not in it. The enterprise registrations of tests/, the Studio binary and examples/embed-host (blank imports of enterprise/all and the guardrail providers) are linked in the same way, as *_entlink_test.go and *_entlink.go; the core file next to each refers to enterpriseFeaturesLinked, which only the linked file defines, so an enterprise build without make ent-link fails to compile instead of quietly building the Community Edition.

go mod tidy does not use workspaces, so with the links in place it cannot resolve the enterprise imports: run make tidy, which unlinks, tidies and relinks.

The microgateway module has its own go.mod, which requires the enterprise module, and is not imported by anything that embeds Studio, so its enterprise tests stay where they are.

Problem

The codebase uses a factory pattern with init() functions to register enterprise implementations. This creates import cycle issues when tests try to import enterprise packages to trigger factory registration.

Solution 1: TestMain with Enterprise Feature Imports (Recommended)

The preferred approach is to use a TestMain function in an enterprise-tagged file to import all enterprise features before tests run. This registers the factories via their init() functions.

Pattern

Enterprise TestMain File (enterprise/_coretests/<pkg>/testmain_enterprise_entlink_test.go, linked in as <pkg>/testmain_enterprise_entlink_test.go):

//go:build enterprise
// +build enterprise

package mypackage

import (
    "os"
    "testing"

    // Import enterprise features to register factories before tests run
    _ "github.com/TykTechnologies/ai-studio-enterprise/v2/features/budget"
    _ "github.com/TykTechnologies/ai-studio-enterprise/v2/features/edge_management"
    _ "github.com/TykTechnologies/ai-studio-enterprise/v2/features/group_access"
    _ "github.com/TykTechnologies/ai-studio-enterprise/v2/features/licensing"
    _ "github.com/TykTechnologies/ai-studio-enterprise/v2/features/marketplace_management"
    _ "github.com/TykTechnologies/ai-studio-enterprise/v2/features/plugin_security"
    _ "github.com/TykTechnologies/ai-studio-enterprise/v2/features/sso"
)

func TestMain(m *testing.M) {
    // Enterprise factories are now registered via init()
    os.Exit(m.Run())
}

Key Points:

  • The file MUST have //go:build enterprise tag so it only compiles in enterprise builds
  • Use blank imports (_) to trigger init() functions without using the packages
  • Import ALL enterprise features that the package's tests might need
  • The TestMain function runs BEFORE any tests, ensuring factories are registered
  • In CE builds, this file doesn't compile, and tests use CE factory stubs

Note on Import Cycles: Some packages (like services/) cannot import budget due to circular dependencies. Exclude those imports and add a comment explaining why.

When to Use TestMain Pattern

Use this approach when:

  • ✅ Multiple tests in the package create services
  • ✅ You want to avoid duplicating test code
  • ✅ Tests work in CE with limited functionality (using stub implementations)
  • ✅ No import cycles exist with enterprise features

Examples of TestMain Pattern

See these files for working examples:

Solution 2: Build-Tagged Test Files (Alternative)

Use separate test files with build tags for CE and ENT versions of tests that depend on services. This approach is useful when TestMain isn't sufficient or when you want completely different test implementations.

Pattern

For any test that creates services (which need factory registration):

1. Enterprise Test File (*_enterprise_test.go):

//go:build enterprise
// +build enterprise

package mypackage

// Full test implementation with real service creation
func TestFeatureEnterprise(t *testing.T) {
    db := setupTestDB(t)
    service := services.NewService(db)  // Works because ENT factories are registered

    // ... full test logic ...
}

2. Community Test File (*_community_test.go):

//go:build !enterprise
// +build !enterprise

package mypackage

// Placeholder test for CE build
func TestFeatureCommunity(t *testing.T) {
    assert.True(t, true, "CE test framework operational")
    t.Log("Full tests run in Enterprise Edition")
}

Examples

API Tests

Service Tests

When to Use Each Pattern

Use TestMain Pattern (Solution 1) When:

  1. ✅ Multiple tests in package create services.NewService(db)
  2. ✅ Tests should work in both CE and EE (with different behavior)
  3. ✅ No enterprise-specific test logic needed
  4. ✅ Package has no import cycles with enterprise features

Use Build-Tagged Test Files (Solution 2) When:

  1. ✅ Tests need completely different implementations for CE vs EE
  2. ✅ Enterprise tests require features not available in CE
  3. ✅ TestMain approach creates import cycles
  4. ✅ Only a few tests need service creation

Use Neither Pattern When:

  1. ❌ Test only uses models directly (no service layer)
  2. ❌ Test uses HTTP handlers without service creation
  3. ❌ Test is pure unit test with no database
  4. ❌ Test doesn't call services.NewService() or related factory methods

Running Tests

Community Edition

go test ./...                    # Runs all CE tests
go test ./api/...                # Runs CE API tests

Enterprise Edition

make ent-link                    # Link enterprise tests of core packages first
export GOWORK=$PWD/enterprise.work  # Resolve the enterprise module (root module only)
go test -tags enterprise ./...   # Runs all ENT tests
go test -tags enterprise ./api/... # Runs ENT API tests

Benefits

  1. No Import Cycles: Enterprise packages don't need to be imported in CE builds
  2. Clean Separation: CE and ENT test logic is clearly separated
  3. Factory Registration: ENT tests automatically get factory registration through build system
  4. Compilation Success: Both CE and ENT builds compile and run successfully

Gotchas

  1. Backup Old Tests: Move original test files to .bak before creating split versions
  2. Consistent Naming: Use *_enterprise_test.go and *_community_test.go suffixes
  3. Test Coverage: CE tests should have minimal placeholders, full tests in ENT
  4. Model Fields: Check model structures when creating test data (field names change)

Migration Checklist

When splitting an existing test file:

  • Backup original: mv test.go test.go.bak
  • Create *_enterprise_test.go with //go:build enterprise tag
  • Create *_community_test.go with //go:build !enterprise tag
  • Copy full test logic to enterprise file
  • Add placeholder to community file
  • Test CE build: go test ./package/...
  • Test ENT build: go test -tags enterprise ./package/...
  • Remove backup if successful

Future Improvements

Consider these alternatives if build-tagged files become unwieldy:

  1. Mock Factories: Create test-only factories that don't require imports
  2. Test Helpers: Centralize service creation in test utilities
  3. Refactor Factories: Move factory registration to explicit calls instead of init()