Skip to content

Hooks

Hooks allow you to modify input before KCL execution and transform results after.

Available Hooks

  • Defaults Hook


    Add default values to all test inputs

  • Filter Hook


    Filter out certain resource types from results

  • Transform Hook


    Transform each result item


Hook Types

Add default values to all test inputs:

defaultsHook := kcltest.NewDefaultsHook(map[string]any{
    "ctx": map[string]any{
        "apiextensions.crossplane.io/environment": map[string]any{
            "region": "eu10",
        },
    },
})

Filter out certain resource types from results:

filterHook := kcltest.NewFilterHook(func(item map[string]any) bool {
    kind, _ := kcltest.GetByPath[string](item, "kind")
    return kind != "Usage"  // Exclude Usage resources
})

Note

Return true to keep the item, false to filter it out.

Transform each result item:

transformHook := kcltest.NewTransformHook(func(item map[string]any) map[string]any {
    item["_testProcessed"] = true
    return item
})

Using Hooks

Single Hook

assertion := kcltest.NewKCLAssertion(t, "../src/main.k").
    WithHooks(defaultsHook).
    WithTestInput(kcltest.WithInputFile("input.yaml"))

Multiple Hooks

assertion := kcltest.NewKCLAssertion(t, "../src/main.k").
    WithHooks(defaultsHook, filterHook, transformHook).
    WithTestInput(kcltest.WithInputFile("input.yaml"))

Chained Hooks

chained := kcltest.ChainHooks(defaultsHook, filterHook, transformHook)

assertion := kcltest.NewKCLAssertion(t, "../src/main.k").
    WithHooks(chained).
    WithTestInput(kcltest.WithInputFile("input.yaml"))

Execution Order

graph LR
    A[Input] --> B[BeforeRun Hooks]
    B --> C[KCL Execution]
    C --> D[AfterRun Hooks]
    D --> E[Result]
Phase Description
BeforeRun Hooks are called in order on the input
KCL Execution KCL is executed with modified input
AfterRun Hooks are called in order on the result

Common Patterns

Environment Defaults

Set up common environment context for all tests:

envHook := kcltest.NewDefaultsHook(map[string]any{
    "ctx": map[string]any{
        "apiextensions.crossplane.io/environment": map[string]any{
            "region":      "eu10",
            "environment": "dev",
            "cluster":     "test-cluster",
        },
    },
})

Exclude Internal Resources

Filter out internal resources that shouldn't be tested directly:

excludeInternal := kcltest.NewFilterHook(func(item map[string]any) bool {
    kind, _ := kcltest.GetByPath[string](item, "kind")
    // Keep everything except Usage and ProviderConfig
    return kind != "Usage" && kind != "ProviderConfig"
})

Add Test Markers

Mark all resources for test identification:

testMarker := kcltest.NewTransformHook(func(item map[string]any) map[string]any {
    annotations, _ := kcltest.GetByPath[map[string]any](item, "metadata.annotations")
    if annotations == nil {
        annotations = make(map[string]any)
    }
    annotations["test.example.com/generated"] = "true"
    kcltest.SetByPath(item, "metadata.annotations", annotations)
    return item
})