354 lines
10 KiB
Go
354 lines
10 KiB
Go
//go:build integration
|
|
// +build integration
|
|
|
|
package policiesdb_test
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
|
|
// Your internal packages
|
|
"github.com/tech/sendico/pkg/db/internal/mongo/policiesdb"
|
|
"github.com/tech/sendico/pkg/db/repository"
|
|
"github.com/tech/sendico/pkg/db/repository/builder"
|
|
"github.com/tech/sendico/pkg/merrors"
|
|
// Model package (contains PolicyDescription + Describable)
|
|
"github.com/tech/sendico/pkg/model"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
// Testcontainers
|
|
"github.com/testcontainers/testcontainers-go"
|
|
"github.com/testcontainers/testcontainers-go/modules/mongodb"
|
|
"github.com/testcontainers/testcontainers-go/wait"
|
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
"go.mongodb.org/mongo-driver/mongo"
|
|
"go.mongodb.org/mongo-driver/mongo/options"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// Helper to terminate container
|
|
func terminate(t *testing.T, ctx context.Context, container *mongodb.MongoDBContainer) {
|
|
err := container.Terminate(ctx)
|
|
require.NoError(t, err, "failed to terminate MongoDB container")
|
|
}
|
|
|
|
// Helper to disconnect client
|
|
func disconnect(t *testing.T, ctx context.Context, client *mongo.Client) {
|
|
err := client.Disconnect(context.Background())
|
|
require.NoError(t, err, "failed to disconnect from MongoDB")
|
|
}
|
|
|
|
// Helper to drop the Policies collection
|
|
func cleanupCollection(t *testing.T, ctx context.Context, db *mongo.Database) {
|
|
// The actual collection name is typically the value returned by
|
|
// (&model.PolicyDescription{}).Collection(), or something similar.
|
|
// Make sure it matches what your code uses (often "policies" or "policyDescription").
|
|
err := db.Collection((&model.PolicyDescription{}).Collection()).Drop(ctx)
|
|
require.NoError(t, err, "failed to drop collection between sub-tests")
|
|
}
|
|
|
|
func TestPoliciesDB(t *testing.T) {
|
|
// Create context with reasonable timeout
|
|
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
|
|
defer cancel()
|
|
|
|
// Start MongoDB test container
|
|
mongoC, err := mongodb.Run(ctx,
|
|
"mongo:latest",
|
|
mongodb.WithUsername("root"),
|
|
mongodb.WithPassword("password"),
|
|
testcontainers.WithWaitStrategy(wait.ForLog("Waiting for connections")),
|
|
)
|
|
require.NoError(t, err, "failed to start MongoDB container")
|
|
defer terminate(t, ctx, mongoC)
|
|
|
|
// Get connection URI
|
|
mongoURI, err := mongoC.ConnectionString(ctx)
|
|
require.NoError(t, err, "failed to get connection string")
|
|
|
|
// Connect client
|
|
clientOpts := options.Client().ApplyURI(mongoURI)
|
|
client, err := mongo.Connect(ctx, clientOpts)
|
|
require.NoError(t, err, "failed to connect to MongoDB")
|
|
defer disconnect(t, ctx, client)
|
|
|
|
// Create test DB
|
|
db := client.Database("testdb")
|
|
|
|
// Use a no-op logger (or real logger if you prefer)
|
|
logger := zap.NewNop()
|
|
|
|
// Create an instance of PoliciesDB
|
|
pdb, err := policiesdb.Create(logger, db)
|
|
require.NoError(t, err, "unexpected error creating PoliciesDB")
|
|
|
|
// ---------------------------------------------------------
|
|
// Each sub-test below starts by dropping the collection.
|
|
// ---------------------------------------------------------
|
|
|
|
t.Run("CreateAndGet", func(t *testing.T) {
|
|
cleanupCollection(t, ctx, db) // ensure no leftover data
|
|
|
|
desc := "Test policy description"
|
|
policy := &model.PolicyDescription{
|
|
Describable: model.Describable{
|
|
Name: "TestPolicy",
|
|
Description: &desc,
|
|
},
|
|
}
|
|
require.NoError(t, pdb.Create(ctx, policy))
|
|
|
|
result := &model.PolicyDescription{}
|
|
err := pdb.Get(ctx, policy.ID, result)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, policy.ID, result.ID)
|
|
assert.Equal(t, "TestPolicy", result.Name)
|
|
assert.NotNil(t, result.Description)
|
|
assert.Equal(t, "Test policy description", *result.Description)
|
|
})
|
|
|
|
t.Run("Get_NotFound", func(t *testing.T) {
|
|
cleanupCollection(t, ctx, db)
|
|
|
|
// Attempt to get a non-existent ID
|
|
nonExistentID := primitive.NewObjectID()
|
|
result := &model.PolicyDescription{}
|
|
err := pdb.Get(ctx, nonExistentID, result)
|
|
assert.Error(t, err)
|
|
assert.True(t, errors.Is(err, merrors.ErrNoData))
|
|
})
|
|
|
|
t.Run("Update", func(t *testing.T) {
|
|
cleanupCollection(t, ctx, db)
|
|
|
|
originalDesc := "Original description"
|
|
policy := &model.PolicyDescription{
|
|
Describable: model.Describable{
|
|
Name: "OriginalName",
|
|
Description: &originalDesc,
|
|
},
|
|
}
|
|
require.NoError(t, pdb.Create(ctx, policy))
|
|
|
|
newDesc := "Updated description"
|
|
policy.Name = "UpdatedName"
|
|
policy.Description = &newDesc
|
|
|
|
err := pdb.Update(ctx, policy)
|
|
require.NoError(t, err)
|
|
|
|
updated := &model.PolicyDescription{}
|
|
err = pdb.Get(ctx, policy.ID, updated)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, "UpdatedName", updated.Name)
|
|
assert.NotNil(t, updated.Description)
|
|
assert.Equal(t, "Updated description", *updated.Description)
|
|
})
|
|
|
|
t.Run("Delete", func(t *testing.T) {
|
|
cleanupCollection(t, ctx, db)
|
|
|
|
desc := "To be deleted"
|
|
policy := &model.PolicyDescription{
|
|
Describable: model.Describable{
|
|
Name: "WillDelete",
|
|
Description: &desc,
|
|
},
|
|
}
|
|
require.NoError(t, pdb.Create(ctx, policy))
|
|
|
|
err := pdb.Delete(ctx, policy.ID)
|
|
require.NoError(t, err)
|
|
|
|
deleted := &model.PolicyDescription{}
|
|
err = pdb.Get(ctx, policy.ID, deleted)
|
|
assert.Error(t, err)
|
|
assert.True(t, errors.Is(err, merrors.ErrNoData))
|
|
})
|
|
|
|
t.Run("DeleteMany", func(t *testing.T) {
|
|
cleanupCollection(t, ctx, db)
|
|
|
|
desc1 := "Will be deleted 1"
|
|
desc2 := "Will be deleted 2"
|
|
pol1 := &model.PolicyDescription{
|
|
Describable: model.Describable{
|
|
Name: "BatchDelete1",
|
|
Description: &desc1,
|
|
},
|
|
}
|
|
pol2 := &model.PolicyDescription{
|
|
Describable: model.Describable{
|
|
Name: "BatchDelete2",
|
|
Description: &desc2,
|
|
},
|
|
}
|
|
require.NoError(t, pdb.Create(ctx, pol1))
|
|
require.NoError(t, pdb.Create(ctx, pol2))
|
|
|
|
q := repository.Query().RegEx(repository.Field("description"), "^Will be deleted", "")
|
|
err := pdb.DeleteMany(ctx, q)
|
|
require.NoError(t, err)
|
|
|
|
res1 := &model.PolicyDescription{}
|
|
err1 := pdb.Get(ctx, pol1.ID, res1)
|
|
assert.Error(t, err1)
|
|
assert.True(t, errors.Is(err1, merrors.ErrNoData))
|
|
|
|
res2 := &model.PolicyDescription{}
|
|
err2 := pdb.Get(ctx, pol2.ID, res2)
|
|
assert.Error(t, err2)
|
|
assert.True(t, errors.Is(err2, merrors.ErrNoData))
|
|
})
|
|
|
|
t.Run("FindOne", func(t *testing.T) {
|
|
cleanupCollection(t, ctx, db)
|
|
|
|
desc := "Unique find test"
|
|
policy := &model.PolicyDescription{
|
|
Describable: model.Describable{
|
|
Name: "FindOneTest",
|
|
Description: &desc,
|
|
},
|
|
}
|
|
require.NoError(t, pdb.Create(ctx, policy))
|
|
|
|
// Match by name == "FindOneTest"
|
|
q := repository.Query().Comparison(repository.Field("name"), builder.Eq, "FindOneTest")
|
|
|
|
found := &model.PolicyDescription{}
|
|
err := pdb.FindOne(ctx, q, found)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, policy.ID, found.ID)
|
|
assert.Equal(t, "FindOneTest", found.Name)
|
|
assert.NotNil(t, found.Description)
|
|
assert.Equal(t, "Unique find test", *found.Description)
|
|
})
|
|
|
|
t.Run("All", func(t *testing.T) {
|
|
cleanupCollection(t, ctx, db)
|
|
|
|
// Insert some policies (orgA, orgB, nil org)
|
|
orgA := primitive.NewObjectID()
|
|
orgB := primitive.NewObjectID()
|
|
|
|
descA := "Org A policy"
|
|
policyA := &model.PolicyDescription{
|
|
Describable: model.Describable{
|
|
Name: "PolicyA",
|
|
Description: &descA,
|
|
},
|
|
OrganizationRef: &orgA, // belongs to orgA
|
|
}
|
|
descB := "Org B policy"
|
|
policyB := &model.PolicyDescription{
|
|
Describable: model.Describable{
|
|
Name: "PolicyB",
|
|
Description: &descB,
|
|
},
|
|
OrganizationRef: &orgB, // belongs to orgB
|
|
}
|
|
descNil := "No org policy"
|
|
policyNil := &model.PolicyDescription{
|
|
Describable: model.Describable{
|
|
Name: "PolicyNil",
|
|
Description: &descNil,
|
|
},
|
|
// nil => built-in
|
|
}
|
|
require.NoError(t, pdb.Create(ctx, policyA))
|
|
require.NoError(t, pdb.Create(ctx, policyB))
|
|
require.NoError(t, pdb.Create(ctx, policyNil))
|
|
|
|
// Suppose the requirement is: "All" returns
|
|
// - policies for the requested org
|
|
// - plus built-in (nil) ones
|
|
resultsA, err := pdb.All(ctx, orgA)
|
|
require.NoError(t, err)
|
|
require.Len(t, resultsA, 2) // orgA + built-in
|
|
|
|
var idsA []primitive.ObjectID
|
|
for _, r := range resultsA {
|
|
idsA = append(idsA, r.ID)
|
|
}
|
|
assert.Contains(t, idsA, policyA.ID)
|
|
assert.Contains(t, idsA, policyNil.ID)
|
|
assert.NotContains(t, idsA, policyB.ID)
|
|
|
|
resultsB, err := pdb.All(ctx, orgB)
|
|
require.NoError(t, err)
|
|
require.Len(t, resultsB, 2) // orgB + built-in
|
|
|
|
var idsB []primitive.ObjectID
|
|
for _, r := range resultsB {
|
|
idsB = append(idsB, r.ID)
|
|
}
|
|
assert.Contains(t, idsB, policyB.ID)
|
|
assert.Contains(t, idsB, policyNil.ID)
|
|
assert.NotContains(t, idsB, policyA.ID)
|
|
})
|
|
|
|
t.Run("Policies", func(t *testing.T) {
|
|
cleanupCollection(t, ctx, db)
|
|
|
|
desc1 := "PolicyOne"
|
|
pol1 := &model.PolicyDescription{
|
|
Describable: model.Describable{
|
|
Name: "PolicyOne",
|
|
Description: &desc1,
|
|
},
|
|
}
|
|
desc2 := "PolicyTwo"
|
|
pol2 := &model.PolicyDescription{
|
|
Describable: model.Describable{
|
|
Name: "PolicyTwo",
|
|
Description: &desc2,
|
|
},
|
|
}
|
|
desc3 := "PolicyThree"
|
|
pol3 := &model.PolicyDescription{
|
|
Describable: model.Describable{
|
|
Name: "PolicyThree",
|
|
Description: &desc3,
|
|
},
|
|
}
|
|
require.NoError(t, pdb.Create(ctx, pol1))
|
|
require.NoError(t, pdb.Create(ctx, pol2))
|
|
require.NoError(t, pdb.Create(ctx, pol3))
|
|
|
|
// 1) Request pol1, pol2
|
|
results12, err := pdb.Policies(ctx, []primitive.ObjectID{pol1.ID, pol2.ID})
|
|
require.NoError(t, err)
|
|
require.Len(t, results12, 2)
|
|
// IDs might be out of order, so we do a set-like check
|
|
var set12 []primitive.ObjectID
|
|
for _, r := range results12 {
|
|
set12 = append(set12, r.ID)
|
|
}
|
|
assert.Contains(t, set12, pol1.ID)
|
|
assert.Contains(t, set12, pol2.ID)
|
|
|
|
// 2) Request pol1, pol3, plus a random ID
|
|
fakeID := primitive.NewObjectID()
|
|
results13Fake, err := pdb.Policies(ctx, []primitive.ObjectID{pol1.ID, pol3.ID, fakeID})
|
|
require.NoError(t, err)
|
|
require.Len(t, results13Fake, 2) // pol1 + pol3 only
|
|
var set13Fake []primitive.ObjectID
|
|
for _, r := range results13Fake {
|
|
set13Fake = append(set13Fake, r.ID)
|
|
}
|
|
assert.Contains(t, set13Fake, pol1.ID)
|
|
assert.Contains(t, set13Fake, pol3.ID)
|
|
|
|
// 3) Request with empty slice => expect no results
|
|
resultsEmpty, err := pdb.Policies(ctx, []primitive.ObjectID{})
|
|
require.NoError(t, err)
|
|
assert.Len(t, resultsEmpty, 0)
|
|
})
|
|
}
|