pulumi/cmd/pulumi-test-language
Fraser Waters c93e553597
Add l2-resource-secret to conformance tests (#17812)
Currently this doesn't behave exactly how we want, nested secret values
aren't correctly wrapped. We plan on fixing that in the engine though so
as a conformance test for languages this test is still valid.
2024-11-20 17:47:01 +00:00
..
providers Add l2-resource-secret to conformance tests (#17812) 2024-11-20 17:47:01 +00:00
testdata Add l2-resource-secret to conformance tests (#17812) 2024-11-20 17:47:01 +00:00
README.md Clean up documentation to support the bridge (#17784) 2024-11-15 14:41:07 +00:00
go.mod Changelog and go.mod updates for v3.140.0 (#17809) 2024-11-19 15:57:55 +00:00
go.sum Bump gRPC dependencies and migrate `grpc.Dial` (#17701) 2024-11-06 18:36:10 +00:00
grpc.go Cover configure (#17508) 2024-10-11 02:23:59 +00:00
interface.go Bump gRPC dependencies and migrate `grpc.Dial` (#17701) 2024-11-06 18:36:10 +00:00
interface_test.go Fix language conformance tests (#17677) 2024-11-05 12:00:42 +00:00
internal_test.go Parse conformance programs to decide which packages are needed for SDKs (#16872) 2024-08-05 12:07:06 +00:00
l1empty_test.go Bump gRPC dependencies and migrate `grpc.Dial` (#17701) 2024-11-06 18:36:10 +00:00
l1main_test.go Bump gRPC dependencies and migrate `grpc.Dial` (#17701) 2024-11-06 18:36:10 +00:00
l2continue_on_error_test.go Bump gRPC dependencies and migrate `grpc.Dial` (#17701) 2024-11-06 18:36:10 +00:00
l2destroy_test.go Bump gRPC dependencies and migrate `grpc.Dial` (#17701) 2024-11-06 18:36:10 +00:00
l2large_test.go Bump gRPC dependencies and migrate `grpc.Dial` (#17701) 2024-11-06 18:36:10 +00:00
l2resourceasset_test.go Bump gRPC dependencies and migrate `grpc.Dial` (#17701) 2024-11-06 18:36:10 +00:00
l2resourcesimple_test.go Bump gRPC dependencies and migrate `grpc.Dial` (#17701) 2024-11-06 18:36:10 +00:00
main.go Add matrix testing (#13705) 2023-09-13 15:17:46 +00:00
runtime_options_test.go Bump gRPC dependencies and migrate `grpc.Dial` (#17701) 2024-11-06 18:36:10 +00:00
snapshot_test.go Add asset/archive to conformance tests and fix engine working dir issues (#16100) 2024-05-02 11:32:54 +00:00
snapshots.go Rewrite conformance test snapshots filewise (#17804) 2024-11-19 13:58:51 +00:00
test_host.go Bump gRPC dependencies and migrate `grpc.Dial` (#17701) 2024-11-06 18:36:10 +00:00
testing.go Add a conformance test for preserving map keys (#17350) 2024-10-11 13:34:46 +00:00
tests.go Add l2-resource-secret to conformance tests (#17812) 2024-11-20 17:47:01 +00:00

README.md

(language-conformance-tests)=

Language conformance tests

Language conformance tests (often just conformance tests) are a class of integration test that assert various properties that should hold true across all language runtimes, in essence providing a specification that language runtimes must conform to. They are structured as follows:

  • The "program under test" is expressed using PCL. The program can specify resources and functions supplied by one or more test resource providers with fixed, known implementations, as well as provider-agnostic entities such as stack outputs.
  • A set of assertions are made about the state of the resources, functions, outputs, etc. in the program before and after it has been executed. These assertions should hold true regardless of the language being used to define and execute the program.

For each test run and language, then:

  • If the test requires one or more providers, SDKs are generated from the relevant test providers, exercising SDK generation for the given language.
  • The PCL program is converted into a program in the target language, exercising program generation for the given language.
  • The generated program is executed using a compiled language host running as a separate process and the assertions are checked, exercising program execution for the given language.
  • Generated code is snapshot tested to ensure that it doesn't change unexpectedly.

Running Language Conformance Tests

To run the language conformance tests, for example for Python, run:

go test github.com/pulumi/pulumi/sdk/python/cmd/pulumi-language-python/v3 -count 1

Note: to run this from the root of the repository, make sure you have an uptodate go.work file by running make work.

The conformance tests are named TestLanguage/${TEST_NAME}, for example TestLanguage/l1-output-string.

Python can generate SDKs in multiple ways, either using setup.py or project.toml to define the python package metadata, as well as using different input types (classes-and-dicts or classes). Python also supports either MyPy or PyRight as typechecker. To ensure we cover all of these options, each test is run in 3 variants (the options are orthogonal to each other, so we don't run all combinations):

  • default (uses setup.py, MyPy and the default input types)
  • toml (uses pyproject.toml, PyRight and classes-and-dicts)
  • classes (uses setup, PyRight and classes)

Test names follow the pattern TestLanguage/${VARIANT}/${TEST_NAME}, for example TestLanguage/classes/l1-output-string.

For Nodejs we have two variants, using TypeScript (forceTsc=false) or plain Javascript (forceTsc=true, so named because the test setup runs tsc on the project so it's runnable as plain Javascript). Tests are named for example TestLanguage/forceTsc=true/l1-output-string or TestLanguage/forceTsc=false/l1-output-string.

To run a single test case:

go test github.com/pulumi/pulumi/sdk/python/cmd/pulumi-language-python/v3 -count 1 -run TestLanguage/classes/l1-output-string

To update the snapshots for generated code, run the tests with the PULUMI_ACCEPT environment variable set to a truthy value:

PULUMI_ACCEPT=1 go test github.com/pulumi/pulumi/sdk/python/cmd/pulumi-language-python/v3 -count 1

Architecture

Test providers are defined in gh-file:pulumi#cmd/pulumi-test-language/providers. PCL programs for language conformance tests are defined in gh-file:pulumi#cmd/pulumi-test-language/testdata. gh-file:pulumi#cmd/pulumi-test-language/tests.go then references these programs and defines the assertions to be made about each. Tests are categorised as follows:

  • L1 tests are those which do not exercise provider code paths and use only the most basic of features (e.g. stack outputs).
  • L2 tests are those which do exercise provider code paths and use things such as custom resources, function invocations, and so on.
  • L3 tests exercise features that require more advanced language support such as first-class functions -- apply is a good example of this.

Each language defines a test function (the language test host) responsible for running its conformance test suite, if it implements one. For core languages whose runtimes are written in Go, this typically lives in a language_test.go file next to the relevant language host executable code -- see for example gh-file:pulumi#sdk/nodejs/cmd/pulumi-language-nodejs/language_test.go for NodeJS/TypeScript and gh-file:pulumi#sdk/python/cmd/pulumi-language-python/language_test.go for Python. This function works as follows:

  • The relevant language runtime (e.g. pulumi-language-nodejs for NodeJS) is booted up as a separate process.
  • The pulumi-test-language executable is booted up, with the language runtime's gRPC server address as a parameter. The test language executable itself exposes a gRPC server which allows clients to e.g. retrieve a list of tests (GetLanguageTests) and execute a test (RunLanguageTest).
  • In preparation for test execution, the language test host retrieves the list of tests from the pulumi-test-language server. It s the core SDK (e.g. @pulumi/pulumi in TypeScript/NodeJS).
  • For each test:
    • SDKs required by the test are generated by calling the language host's method. The generated code is written to a temporary directory where it is ed for use in the test.
    • The method is invoked to convert the test's PCL code into a program in the target language. Dependencies are installed with and the test program is .
    • Assertions are verified and the next test is processed until there are no more remaining.
:caption: The lifecycle of a language conformance test suite
:zoom:

sequenceDiagram
    participant LTH as Language test host
    participant PTL as pulumi-test-language
    participant LH as Language host

    LTH->>+PTL: Start pulumi-test-language process
    PTL-->>-LTH: Return gRPC server address

    note right of LTH: All future calls to<br>pulumi-test-language are via gRPC

    LTH->>+PTL: GetLanguageTests()
    PTL-->>-LTH: List of test names

    LTH->>+LH: Start language host
    LH-->>-LTH: Return gRPC server address

    note right of LTH: All future calls to<br>language host are via gRPC

    LTH->>+PTL: PrepareLanguageTests(Language host)
    PTL->>+LH: Pack(Core SDK)
    LH-->>-PTL: Name of core SDK artifact
    PTL->>-LTH: Token

    loop For each test
        LTH->>+PTL: RunLanguageTest(Token, Language test host)

        loop For each SDK
            PTL->>+LH: GeneratePackage(SDK)
            LH-->>-PTL: Write package code to temporary directory
            PTL->>PTL: Verify SDK snapshot
            PTL->>+LH: Pack(SDK)
            LH-->>-PTL: Name of SDK artifact
        end

        PTL->>+LH: GenerateProject(Test)
        LH-->>-PTL: Write project code to temporary directory
        PTL->>PTL: Verify project snapshot

        PTL->>+LH: InstallDependencies(Project)
        LH-->>-PTL: Dependencies installed

        note right of PTL: Execute test with engine

        PTL->>+LH: Run(Project)
        LH-->>-PTL: Run result
        PTL->>PTL: Check assertions against run result and snapshot

        PTL-->>-LTH: Test result
    end

Meta tests

This module contains a number of _test.go files. These are tests of the conformance test system itself. The actual conformance tests are all defined in tests.go.