tests
standalone dagger function so it can be invoked individually duringTDD; All wires them up for parallel execution under
`dagger call all`.
Every password, cluster name, and table name is minted at runtime via
dag.Random().Sha256. Role and database deliberately use the postgres
module's defaults ("postgres"), which a few tests assert against.
Installation
dagger install github.com/z5labs/devex/daggerverse/postgres/tests@a17bf411228ae3d544c7c18bcf08dd04c295d453Entrypoint
Return Type
Tests Example
dagger -m github.com/z5labs/devex/daggerverse/postgres/tests@a17bf411228ae3d544c7c18bcf08dd04c295d453 call \
func (m *MyModule) Example() *dagger.Tests {
return dag.
Tests()
}@function
def example() -> dagger.Tests:
return (
dag.tests()
)@func()
example(): Tests {
return dag
.tests()
}Types
Tests 🔗
all() 🔗
All runs every postgres test as a convenience for local dagger call
all invocations. CI does NOT call All: each of the two
sub-aggregators below (Validation, Cluster) is registered as its own
check, so GH Actions schedules each onto its own runner in parallel —
running All on top would double-bill the same work.
Return Type
Void !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| parallel | Integer ! | 0 | No description provided |
Example
dagger -m github.com/z5labs/devex/daggerverse/postgres/tests@a17bf411228ae3d544c7c18bcf08dd04c295d453 call \
all --parallel integerfunc (m *MyModule) Example(ctx context.Context, parallel int) {
return dag.
Tests().
All(ctx, parallel)
}@function
async def example(parallel: int) -> None:
return await (
dag.tests()
.all(parallel)
)@func()
async example(parallel: number): Promise<void> {
return dag
.tests()
.all(parallel)
}applyFileRoundTrip() 🔗
ApplyFileRoundTrip runs a multi-statement *dagger.File and confirms the resulting rows are readable via Scalar.
Return Type
Void ! Example
dagger -m github.com/z5labs/devex/daggerverse/postgres/tests@a17bf411228ae3d544c7c18bcf08dd04c295d453 call \
apply-file-round-tripfunc (m *MyModule) Example(ctx context.Context) {
return dag.
Tests().
Applyfileroundtrip(ctx)
}@function
async def example() -> None:
return await (
dag.tests()
.applyfileroundtrip()
)@func()
async example(): Promise<void> {
return dag
.tests()
.applyFileRoundTrip()
}bindPrimaryReachableFromAlpine() 🔗
BindPrimaryReachableFromAlpine verifies BindPrimary makes the primary reachable at Cluster.Endpoint() from a fresh alpine container. Alpine lacks pg_isready, so we prove TCP reachability with busybox nc.
Return Type
Void ! Example
dagger -m github.com/z5labs/devex/daggerverse/postgres/tests@a17bf411228ae3d544c7c18bcf08dd04c295d453 call \
bind-primary-reachable-from-alpinefunc (m *MyModule) Example(ctx context.Context) {
return dag.
Tests().
Bindprimaryreachablefromalpine(ctx)
}@function
async def example() -> None:
return await (
dag.tests()
.bindprimaryreachablefromalpine()
)@func()
async example(): Promise<void> {
return dag
.tests()
.bindPrimaryReachableFromAlpine()
}bindPrimaryResolvesFromUserContainerTls() 🔗
BindPrimaryResolvesFromUserContainerTls binds a TLS primary into an alpine container running psql: a verify-full connection with the right CA succeeds, and the same connection without sslrootcert fails (it cannot verify the server).
Return Type
Void ! Example
dagger -m github.com/z5labs/devex/daggerverse/postgres/tests@a17bf411228ae3d544c7c18bcf08dd04c295d453 call \
bind-primary-resolves-from-user-container-tlsfunc (m *MyModule) Example(ctx context.Context) {
return dag.
Tests().
Bindprimaryresolvesfromusercontainertls(ctx)
}@function
async def example() -> None:
return await (
dag.tests()
.bindprimaryresolvesfromusercontainertls()
)@func()
async example(): Promise<void> {
return dag
.tests()
.bindPrimaryResolvesFromUserContainerTls()
}clientPingWrongPasswordFails() 🔗
ClientPingWrongPasswordFails verifies a correct-password Ping succeeds and a wrong-password Ping fails with an auth error.
Return Type
Void ! Example
dagger -m github.com/z5labs/devex/daggerverse/postgres/tests@a17bf411228ae3d544c7c18bcf08dd04c295d453 call \
client-ping-wrong-password-failsfunc (m *MyModule) Example(ctx context.Context) {
return dag.
Tests().
Clientpingwrongpasswordfails(ctx)
}@function
async def example() -> None:
return await (
dag.tests()
.clientpingwrongpasswordfails()
)@func()
async example(): Promise<void> {
return dag
.tests()
.clientPingWrongPasswordFails()
}cluster() 🔗
Cluster runs the topology and client round-trip tests. Each test boots its own cluster via bootCluster, whose runtime-random name folds into Postgres.Cluster’s session-cache key so concurrent tests boot independent backing services and never share storage.
Return Type
Void !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| parallel | Integer ! | 0 | No description provided |
Example
dagger -m github.com/z5labs/devex/daggerverse/postgres/tests@a17bf411228ae3d544c7c18bcf08dd04c295d453 call \
cluster --parallel integerfunc (m *MyModule) Example(ctx context.Context, parallel int) {
return dag.
Tests().
Cluster(ctx, parallel)
}@function
async def example(parallel: int) -> None:
return await (
dag.tests()
.cluster(parallel)
)@func()
async example(parallel: number): Promise<void> {
return dag
.tests()
.cluster(parallel)
}clusterMtlsRoundTripFromClient() 🔗
ClusterMtlsRoundTripFromClient boots a mutual-TLS primary and proves a matching mTLS client (presenting a client cert signed by the trusted CA) can round-trip Exec
Return Type
Void ! Example
dagger -m github.com/z5labs/devex/daggerverse/postgres/tests@a17bf411228ae3d544c7c18bcf08dd04c295d453 call \
cluster-mtls-round-trip-from-clientfunc (m *MyModule) Example(ctx context.Context) {
return dag.
Tests().
Clustermtlsroundtripfromclient(ctx)
}@function
async def example() -> None:
return await (
dag.tests()
.clustermtlsroundtripfromclient()
)@func()
async example(): Promise<void> {
return dag
.tests()
.clusterMtlsRoundTripFromClient()
}clusterRejectsNilPassword() 🔗
ClusterRejectsNilPassword verifies a nil password is rejected.
Return Type
Void ! Example
dagger -m github.com/z5labs/devex/daggerverse/postgres/tests@a17bf411228ae3d544c7c18bcf08dd04c295d453 call \
cluster-rejects-nil-passwordfunc (m *MyModule) Example(ctx context.Context) {
return dag.
Tests().
Clusterrejectsnilpassword(ctx)
}@function
async def example() -> None:
return await (
dag.tests()
.clusterrejectsnilpassword()
)@func()
async example(): Promise<void> {
return dag
.tests()
.clusterRejectsNilPassword()
}clusterRejectsNilSecurity() 🔗
ClusterRejectsNilSecurity verifies a nil clientListenerSecurity is rejected.
Return Type
Void ! Example
dagger -m github.com/z5labs/devex/daggerverse/postgres/tests@a17bf411228ae3d544c7c18bcf08dd04c295d453 call \
cluster-rejects-nil-securityfunc (m *MyModule) Example(ctx context.Context) {
return dag.
Tests().
Clusterrejectsnilsecurity(ctx)
}@function
async def example() -> None:
return await (
dag.tests()
.clusterrejectsnilsecurity()
)@func()
async example(): Promise<void> {
return dag
.tests()
.clusterRejectsNilSecurity()
}clusterTlsRoundTripFromClient() 🔗
ClusterTlsRoundTripFromClient boots a one-way-TLS primary and proves a matching TLS client can Exec + Scalar against it over the encrypted listener.
Return Type
Void ! Example
dagger -m github.com/z5labs/devex/daggerverse/postgres/tests@a17bf411228ae3d544c7c18bcf08dd04c295d453 call \
cluster-tls-round-trip-from-clientfunc (m *MyModule) Example(ctx context.Context) {
return dag.
Tests().
Clustertlsroundtripfromclient(ctx)
}@function
async def example() -> None:
return await (
dag.tests()
.clustertlsroundtripfromclient()
)@func()
async example(): Promise<void> {
return dag
.tests()
.clusterTlsRoundTripFromClient()
}defaultsProduceHealthyPrimary() 🔗
DefaultsProduceHealthyPrimary boots a default cluster and proves it is
a healthy primary by running pg_isready against it from a container
running the postgres image (which ships pg_isready), bound via
BindPrimary.
Return Type
Void ! Example
dagger -m github.com/z5labs/devex/daggerverse/postgres/tests@a17bf411228ae3d544c7c18bcf08dd04c295d453 call \
defaults-produce-healthy-primaryfunc (m *MyModule) Example(ctx context.Context) {
return dag.
Tests().
Defaultsproducehealthyprimary(ctx)
}@function
async def example() -> None:
return await (
dag.tests()
.defaultsproducehealthyprimary()
)@func()
async example(): Promise<void> {
return dag
.tests()
.defaultsProduceHealthyPrimary()
}endpointShouldNotBeCached() 🔗
EndpointShouldNotBeCached verifies the chained cluster methods re-execute on every call rather than freezing on a cached result. Endpoint is a pure address getter, so we exercise the re-execution that matters: Ping (which starts the service), Stop (which kills it), then Ping again — the second Ping must re-start the killed service. If Client/start were cached, the service would stay dead and the second Ping would dial a hung port. We also assert the Endpoint address is stable across the cycle.
Return Type
Void ! Example
dagger -m github.com/z5labs/devex/daggerverse/postgres/tests@a17bf411228ae3d544c7c18bcf08dd04c295d453 call \
endpoint-should-not-be-cachedfunc (m *MyModule) Example(ctx context.Context) {
return dag.
Tests().
Endpointshouldnotbecached(ctx)
}@function
async def example() -> None:
return await (
dag.tests()
.endpointshouldnotbecached()
)@func()
async example(): Promise<void> {
return dag
.tests()
.endpointShouldNotBeCached()
}execScalarRoundTrip() 🔗
ExecScalarRoundTrip runs a CREATE TABLE + INSERT + SELECT count(*) sequence across chained Cluster.Client() calls, proving the session-cached cluster preserves on-disk state between separate Client handles.
Return Type
Void ! Example
dagger -m github.com/z5labs/devex/daggerverse/postgres/tests@a17bf411228ae3d544c7c18bcf08dd04c295d453 call \
exec-scalar-round-tripfunc (m *MyModule) Example(ctx context.Context) {
return dag.
Tests().
Execscalarroundtrip(ctx)
}@function
async def example() -> None:
return await (
dag.tests()
.execscalarroundtrip()
)@func()
async example(): Promise<void> {
return dag
.tests()
.execScalarRoundTrip()
}mtlsClusterRejectsTlsOnlyClient() 🔗
MtlsClusterRejectsTlsOnlyClient boots an mTLS primary (started via a valid mTLS client so the service is ready), then dials it with a TLS-only standalone client that presents no client certificate. The standalone Postgres.Client has no cluster reference, so it bypasses the coupling check and reaches the wire, where the listener’s clientcert=verify-full rejects it with a TLS/cert error.
Return Type
Void ! Example
dagger -m github.com/z5labs/devex/daggerverse/postgres/tests@a17bf411228ae3d544c7c18bcf08dd04c295d453 call \
mtls-cluster-rejects-tls-only-clientfunc (m *MyModule) Example(ctx context.Context) {
return dag.
Tests().
Mtlsclusterrejectstlsonlyclient(ctx)
}@function
async def example() -> None:
return await (
dag.tests()
.mtlsclusterrejectstlsonlyclient()
)@func()
async example(): Promise<void> {
return dag
.tests()
.mtlsClusterRejectsTlsOnlyClient()
}passwordReusableViaClient() 🔗
PasswordReusableViaClient verifies Cluster.Password() returns a secret whose plaintext equals the provisioning password: re-using it via Postgres.Client against the same endpoint authenticates successfully.
Return Type
Void ! Example
dagger -m github.com/z5labs/devex/daggerverse/postgres/tests@a17bf411228ae3d544c7c18bcf08dd04c295d453 call \
password-reusable-via-clientfunc (m *MyModule) Example(ctx context.Context) {
return dag.
Tests().
Passwordreusableviaclient(ctx)
}@function
async def example() -> None:
return await (
dag.tests()
.passwordreusableviaclient()
)@func()
async example(): Promise<void> {
return dag
.tests()
.passwordReusableViaClient()
}queryJsonreturnsRowObjects() 🔗
QueryJSONReturnsRowObjects verifies QueryJSON returns a *dagger.File whose contents parse as a JSON array of row objects keyed by column name.
Return Type
Void ! Example
dagger -m github.com/z5labs/devex/daggerverse/postgres/tests@a17bf411228ae3d544c7c18bcf08dd04c295d453 call \
query-jsonreturns-row-objectsfunc (m *MyModule) Example(ctx context.Context) {
return dag.
Tests().
Queryjsonreturnsrowobjects(ctx)
}@function
async def example() -> None:
return await (
dag.tests()
.queryjsonreturnsrowobjects()
)@func()
async example(): Promise<void> {
return dag
.tests()
.queryJsonreturnsRowObjects()
}scalarShouldNotBeCached() 🔗
ScalarShouldNotBeCached verifies Scalar re-executes on every call. We insert one row, read count() == “1”, insert a second row, then read count() again: a cached Scalar would still report “1” instead of “2”.
Return Type
Void ! Example
dagger -m github.com/z5labs/devex/daggerverse/postgres/tests@a17bf411228ae3d544c7c18bcf08dd04c295d453 call \
scalar-should-not-be-cachedfunc (m *MyModule) Example(ctx context.Context) {
return dag.
Tests().
Scalarshouldnotbecached(ctx)
}@function
async def example() -> None:
return await (
dag.tests()
.scalarshouldnotbecached()
)@func()
async example(): Promise<void> {
return dag
.tests()
.scalarShouldNotBeCached()
}security() 🔗
Security runs the TLS / mTLS listener + client tests. Each test mints its own CA, leaf certs, password, and cluster name at runtime (no literal credentials or PEM blobs), and folds a unique name into the cluster’s session-cache key, so the tests fan out without sharing state.
Return Type
Void !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| parallel | Integer ! | 0 | No description provided |
Example
dagger -m github.com/z5labs/devex/daggerverse/postgres/tests@a17bf411228ae3d544c7c18bcf08dd04c295d453 call \
security --parallel integerfunc (m *MyModule) Example(ctx context.Context, parallel int) {
return dag.
Tests().
Security(ctx, parallel)
}@function
async def example(parallel: int) -> None:
return await (
dag.tests()
.security(parallel)
)@func()
async example(parallel: number): Promise<void> {
return dag
.tests()
.security(parallel)
}tlsClusterRejectsEmptyName() 🔗
TlsClusterRejectsEmptyName verifies a TLS cluster rejects an empty
name. The cluster hostname — and therefore the SAN the server cert
must carry — derives from name alone, so an empty name would
collapse every TLS/mTLS cluster onto the same sha256(“”) host and
invite cert/SAN reuse. The guard fires in the constructor, before any
service starts, so a placeholder SAN on the cert is fine here.
Return Type
Void ! Example
dagger -m github.com/z5labs/devex/daggerverse/postgres/tests@a17bf411228ae3d544c7c18bcf08dd04c295d453 call \
tls-cluster-rejects-empty-namefunc (m *MyModule) Example(ctx context.Context) {
return dag.
Tests().
Tlsclusterrejectsemptyname(ctx)
}@function
async def example() -> None:
return await (
dag.tests()
.tlsclusterrejectsemptyname()
)@func()
async example(): Promise<void> {
return dag
.tests()
.tlsClusterRejectsEmptyName()
}tlsClusterRejectsPlaintextClient() 🔗
TlsClusterRejectsPlaintextClient verifies the mode-coupling check: asking a TLS cluster for a plaintext client returns an error naming both modes, before any wire activity.
Return Type
Void ! Example
dagger -m github.com/z5labs/devex/daggerverse/postgres/tests@a17bf411228ae3d544c7c18bcf08dd04c295d453 call \
tls-cluster-rejects-plaintext-clientfunc (m *MyModule) Example(ctx context.Context) {
return dag.
Tests().
Tlsclusterrejectsplaintextclient(ctx)
}@function
async def example() -> None:
return await (
dag.tests()
.tlsclusterrejectsplaintextclient()
)@func()
async example(): Promise<void> {
return dag
.tests()
.tlsClusterRejectsPlaintextClient()
}userDatabaseRoundTrip() 🔗
UserDatabaseRoundTrip verifies Cluster.User()/Database() echo the inputs and a pgx round-trip confirms current_user / current_database match.
Return Type
Void ! Example
dagger -m github.com/z5labs/devex/daggerverse/postgres/tests@a17bf411228ae3d544c7c18bcf08dd04c295d453 call \
user-database-round-tripfunc (m *MyModule) Example(ctx context.Context) {
return dag.
Tests().
Userdatabaseroundtrip(ctx)
}@function
async def example() -> None:
return await (
dag.tests()
.userdatabaseroundtrip()
)@func()
async example(): Promise<void> {
return dag
.tests()
.userDatabaseRoundTrip()
}validation() 🔗
Validation runs the input-rejection tests plus the cache-directive tests (*ShouldNotBeCached). These don’t share session-cached cluster state with one another, so they’re safe to fan out unbounded.
Return Type
Void !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| parallel | Integer ! | 0 | No description provided |
Example
dagger -m github.com/z5labs/devex/daggerverse/postgres/tests@a17bf411228ae3d544c7c18bcf08dd04c295d453 call \
validation --parallel integerfunc (m *MyModule) Example(ctx context.Context, parallel int) {
return dag.
Tests().
Validation(ctx, parallel)
}@function
async def example(parallel: int) -> None:
return await (
dag.tests()
.validation(parallel)
)@func()
async example(parallel: number): Promise<void> {
return dag
.tests()
.validation(parallel)
}