Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ dotenv: ['.test.env']
tasks:

### Utility tasks. ###
default:
default:
deps: [build, check-license, check-fmt, check-modules, lint, test-short]

add-license: bash etc/check_license.sh -a
Expand All @@ -36,15 +36,15 @@ tasks:

build-aws-ecs-test: go test -c ./internal/test/aws -o aws.testbin

cross-compile:
cross-compile:
- GOOS=linux GOARCH=386 go build ./...
- GOOS=linux GOARCH=arm go build ./...
- GOOS=linux GOARCH=arm64 go build ./...
- GOOS=linux GOARCH=amd64 go build ./...
- GOOS=linux GOARCH=ppc64le go build ./...
- GOOS=linux GOARCH=s390x go build ./...

check-fmt:
check-fmt:
deps: [install-lll]
cmds:
- bash etc/check_fmt.sh
Expand All @@ -57,9 +57,9 @@ tasks:

api-report: bash etc/api_report.sh

install-libmongocrypt:
install-libmongocrypt:
cmds: [bash etc/install-libmongocrypt.sh]
status:
status:
- test -d install || test -d /cygdrive/c/libmongocrypt/bin

run-docker: bash etc/run_docker.sh
Expand All @@ -76,7 +76,7 @@ tasks:
# specific operating systems or architectures. For example, staticcheck will only check for 64-bit
# alignment of atomically accessed variables on 32-bit architectures (see
# https://staticcheck.io/docs/checks#SA1027)
lint:
lint:
cmds:
- GOOS=linux GOARCH=386 etc/golangci-lint.sh
- GOOS=linux GOARCH=arm etc/golangci-lint.sh
Expand Down Expand Up @@ -104,8 +104,8 @@ tasks:

test-oidc-remote: bash etc/run-oidc-remote-test.sh

test-atlas-connect:
- go test -v -run ^TestAtlas$ go.mongodb.org/mongo-driver/v2/internal/cmd/testatlas -args "$ATLAS_REPL" "$ATLAS_SHRD" "$ATLAS_FREE" "$ATLAS_TLS11" "$ATLAS_TLS12" "$ATLAS_SERVERLESS" "$ATLAS_SRV_REPL" "$ATLAS_SRV_SHRD" "$ATLAS_SRV_FREE" "$ATLAS_SRV_TLS11" "$ATLAS_SRV_TLS12" "$ATLAS_SRV_SERVERLESS" >> test.suite
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems none of the URI env vars were previously populated, effectively making this test a no-op. It's unclear when that change happened because the test passes silently.

Copy link
Member Author

@prestonvasquez prestonvasquez Aug 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see that now, thanks for the clarification!

test-atlas-connect:
- go test -v -run ^TestAtlas$ go.mongodb.org/mongo-driver/v2/internal/cmd/testatlas >> test.suite

test-awskms: bash etc/run-awskms-test.sh

Expand All @@ -117,9 +117,9 @@ tasks:

### Local FaaS tasks. ###
build-faas-awslambda:
requires:
requires:
vars: [MONGODB_URI]
cmds:
cmds:
- make -c internal/cmd/faas/awslambda

### Evergreen specific tasks. ###
Expand All @@ -134,7 +134,7 @@ tasks:
- ATLAS_DATA_LAKE_INTEGRATION_TEST=true go test -v ./internal/integration/unified -run TestUnifiedSpec/atlas-data-lake-testing >> spec_test.suite
- ATLAS_DATA_LAKE_INTEGRATION_TEST=true go test -v ./internal/integration -run TestAtlasDataLake >> spec_test.suite

evg-test-enterprise-auth:
evg-test-enterprise-auth:
- go run -tags gssapi ./internal/cmd/testentauth/main.go

evg-test-oidc-auth:
Expand Down Expand Up @@ -188,15 +188,15 @@ tasks:
### Benchmark specific tasks and support. ###
benchmark:
deps: [perf-files]
cmds:
cmds:
- go test ${BUILD_TAGS} -benchmem -bench=. ./benchmark | test benchmark.suite

driver-benchmark:
cmds:
driver-benchmark:
cmds:
- go test ./internal/cmd/benchmark -v --fullRun | tee perf.suite

### Internal tasks. ###
install-lll:
install-lll:
internal: true
cmds:
- go install github.com/walle/lll/...@latest
17 changes: 17 additions & 0 deletions internal/assert/assertions.go
Original file line number Diff line number Diff line change
Expand Up @@ -1073,3 +1073,20 @@ func buildErrorChainString(err error) string {
}
return chain
}

// NotEmpty asserts that the specified object is NOT [Empty].
//
// if assert.NotEmpty(t, obj) {
// assert.Equal(t, "two", obj[1])
// }
func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
pass := !isEmpty(object)
if !pass {
if h, ok := t.(tHelper); ok {
h.Helper()
}
Fail(t, fmt.Sprintf("Should NOT be empty, but was %v", object), msgAndArgs...)
}

return pass
}
223 changes: 188 additions & 35 deletions internal/cmd/testatlas/atlas_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,56 +8,161 @@ package main

import (
"context"
"crypto/tls"
"encoding/base64"
"errors"
"flag"
"fmt"
"net/url"
"os"
"path/filepath"
"testing"
"time"

"go.mongodb.org/mongo-driver/v2/bson"
"go.mongodb.org/mongo-driver/v2/internal/assert"
"go.mongodb.org/mongo-driver/v2/internal/handshake"
"go.mongodb.org/mongo-driver/v2/internal/require"
"go.mongodb.org/mongo-driver/v2/mongo"
"go.mongodb.org/mongo-driver/v2/mongo/options"
)

func TestMain(m *testing.M) {
flag.Parse()
os.Exit(m.Run())
}

func TestAtlas(t *testing.T) {
uris := flag.Args()
ctx := context.Background()

t.Logf("Running atlas tests for %d uris\n", len(uris))

for idx, uri := range uris {
t.Logf("Running test %d\n", idx)

// Set a low server selection timeout so we fail fast if there are errors.
clientOpts := options.Client().
ApplyURI(uri).
SetServerSelectionTimeout(1 * time.Second)

// Run basic connectivity test.
if err := runTest(ctx, clientOpts); err != nil {
t.Fatalf("error running test with TLS at index %d: %v", idx, err)
}

tlsConfigSkipVerify := clientOpts.TLSConfig
tlsConfigSkipVerify.InsecureSkipVerify = true

// Run the connectivity test with InsecureSkipVerify to ensure SNI is done correctly even if verification is
// disabled.
clientOpts.SetTLSConfig(tlsConfigSkipVerify)

if err := runTest(ctx, clientOpts); err != nil {
t.Fatalf("error running test with tlsInsecure at index %d: %v", idx, err)
}
cases := []struct {
name string
envVar string
certKeyFile string
wantErr string
}{
{
name: "Atlas with TLS",
envVar: "ATLAS_REPL",
certKeyFile: "",
wantErr: "",
},
{
name: "Atlas with TLS and shared cluster",
envVar: "ATLAS_SHRD",
certKeyFile: "",
wantErr: "",
},
{
name: "Atlas with free tier",
envVar: "ATLAS_FREE",
certKeyFile: "",
wantErr: "",
},
{
name: "Atlas with TLS 1.1",
envVar: "ATLAS_TLS11",
certKeyFile: "",
wantErr: "",
},
{
name: "Atlas with TLS 1.2",
envVar: "ATLAS_TLS12",
certKeyFile: "",
wantErr: "",
},
{
name: "Atlas with serverless",
envVar: "ATLAS_SERVERLESS",
certKeyFile: "",
wantErr: "",
},
{
name: "Atlas with srv file on replica set",
envVar: "ATLAS_SRV_REPL",
certKeyFile: "",
wantErr: "",
},
{
name: "Atlas with srv file on shared cluster",
envVar: "ATLAS_SRV_SHRD",
certKeyFile: "",
wantErr: "",
},
{
name: "Atlas with srv file on free tier",
envVar: "ATLAS_SRV_FREE",
certKeyFile: "",
wantErr: "",
},
{
name: "Atlas with srv file on TLS 1.1",
envVar: "ATLAS_SRV_TLS11",
certKeyFile: "",
wantErr: "",
},
{
name: "Atlas with srv file on TLS 1.2",
envVar: "ATLAS_SRV_TLS12",
certKeyFile: "",
wantErr: "",
},
{
name: "Atlas with srv file on serverless",
envVar: "ATLAS_SRV_SERVERLESS",
certKeyFile: "",
wantErr: "",
},
{
name: "Atlas with X509 Dev",
envVar: "ATLAS_X509_DEV",
certKeyFile: createAtlasX509DevCertKeyFile(t),
wantErr: "",
},
{
name: "Atlas with X509 Dev no user",
envVar: "ATLAS_X509_DEV",
certKeyFile: createAtlasX509DevCertKeyFileNoUser(t),
wantErr: "UserNotFound",
},
}

t.Logf("Finished!")
for _, tc := range cases {
t.Run(fmt.Sprintf("%s (%s)", tc.name, tc.envVar), func(t *testing.T) {
uri := os.Getenv(tc.envVar)
if uri == "" {
t.Skipf("Environment variable %q is not set", tc.envVar)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skipping if the env vars aren't set will cause the tests to silently pass if the env vars somehow get unset, even if there is a bug. Is there a way to not skip in this case?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can add a build tag and remove the skip option.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds like a great solution!


if tc.certKeyFile != "" {
uri = addTLSCertKeyFile(t, tc.certKeyFile, uri)
}

// Set a low server selection timeout so we fail fast if there are errors.
clientOpts := options.Client().
ApplyURI(uri).
SetServerSelectionTimeout(1 * time.Second)

// Run basic connectivity test.
err := runTest(context.Background(), clientOpts)
if tc.wantErr != "" {
assert.ErrorContains(t, err, tc.wantErr, "expected error to contain %q", tc.wantErr)

return
}
require.NoError(t, err, "error running test with TLS")

orig := clientOpts.TLSConfig
if orig == nil {
orig = &tls.Config{}
}

insecure := orig.Clone()
insecure.InsecureSkipVerify = true

// Run the connectivity test with InsecureSkipVerify to ensure SNI is done
// correctly even if verification is disabled.
insecureClientOpts := options.Client().
ApplyURI(uri).
SetServerSelectionTimeout(1 * time.Second).
SetTLSConfig(insecure)

err = runTest(context.Background(), insecureClientOpts)
require.NoError(t, err, "error running test with tlsInsecure")
})
}
}

func runTest(ctx context.Context, clientOpts *options.ClientOptions) error {
Expand All @@ -83,3 +188,51 @@ func runTest(ctx context.Context, clientOpts *options.ClientOptions) error {
}
return nil
}

func createAtlasX509DevCertKeyFile(t *testing.T) string {
t.Helper()

b64 := os.Getenv("ATLAS_X509_DEV_CERT_BASE64")
assert.NotEmpty(t, b64, "Environment variable ATLAS_X509_DEV_CERT_BASE64 is not set")

certBytes, err := base64.StdEncoding.DecodeString(b64)
require.NoError(t, err, "failed to decode ATLAS_X509_DEV_CERT_BASE64")

certFilePath := t.TempDir() + "/atlas_x509_dev_cert.pem"

err = os.WriteFile(certFilePath, certBytes, 0600)
require.NoError(t, err, "failed to write ATLAS_X509_DEV_CERT_BASE64 to file")

return certFilePath
}

func createAtlasX509DevCertKeyFileNoUser(t *testing.T) string {
t.Helper()

b64 := os.Getenv("ATLAS_X509_DEV_CERT_NOUSER_BASE64")
assert.NotEmpty(t, b64, "Environment variable ATLAS_X509_DEV_CERT_NOUSER_BASE64 is not set")

keyBytes, err := base64.StdEncoding.DecodeString(b64)
require.NoError(t, err, "failed to decode ATLAS_X509_DEV_CERT_NOUSER_BASE64")

keyFilePath := t.TempDir() + "/atlas_x509_dev_cert_no_user.pem"

err = os.WriteFile(keyFilePath, keyBytes, 0600)
require.NoError(t, err, "failed to write ATLAS_X509_DEV_CERT_NOUSER_BASE64 to file")

return keyFilePath
}

func addTLSCertKeyFile(t *testing.T, certKeyFile, uri string) string {
t.Helper()

u, err := url.Parse(uri)
require.NoError(t, err, "failed to parse uri")

q := u.Query()
q.Set("tlsCertificateKeyFile", filepath.ToSlash(certKeyFile))

u.RawQuery = q.Encode()

return u.String()
}
Loading