Dagger
Search

tests

standalone dagger function so it can be invoked individually during
TDD; 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@a17bf411228ae3d544c7c18bcf08dd04c295d453

Entrypoint

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
NameTypeDefault ValueDescription
parallelInteger !0No description provided
Example
dagger -m github.com/z5labs/devex/daggerverse/postgres/tests@a17bf411228ae3d544c7c18bcf08dd04c295d453 call \
 all --parallel integer
func (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-trip
func (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-alpine
func (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-tls
func (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-fails
func (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
NameTypeDefault ValueDescription
parallelInteger !0No description provided
Example
dagger -m github.com/z5labs/devex/daggerverse/postgres/tests@a17bf411228ae3d544c7c18bcf08dd04c295d453 call \
 cluster --parallel integer
func (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-client
func (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-password
func (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-security
func (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-client
func (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-primary
func (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-cached
func (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-trip
func (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-client
func (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-client
func (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-objects
func (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-cached
func (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
NameTypeDefault ValueDescription
parallelInteger !0No description provided
Example
dagger -m github.com/z5labs/devex/daggerverse/postgres/tests@a17bf411228ae3d544c7c18bcf08dd04c295d453 call \
 security --parallel integer
func (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-name
func (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-client
func (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-trip
func (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
NameTypeDefault ValueDescription
parallelInteger !0No description provided
Example
dagger -m github.com/z5labs/devex/daggerverse/postgres/tests@a17bf411228ae3d544c7c18bcf08dd04c295d453 call \
 validation --parallel integer
func (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)
}