diff --git a/executors/mongo/README.md b/executors/mongo/README.md index 0ed618ca..5100c700 100644 --- a/executors/mongo/README.md +++ b/executors/mongo/README.md @@ -19,6 +19,33 @@ Example: } ``` +### Load fixtures + +```yaml +- type: mongo + uri: mongodb://localhost:27017 + database: my-database + actions: + - type: loadFixtures + folder: fixtures/ +``` + +This action will first **drop all the collections in the database**, and then load multiple collections at once from a folder. +The fixtures folder must contain one file per collection, and be named after the collection. For example, `cards.yml` will create a `cards` collection. +The items in the collections are declared as a YAML array. For example: + +```yaml +# fixtures/cards.yml +- suit: clubs + value: jack + +- suit: clubs + value: queen + +- suit: clubs + value: king +``` + ### Insert documents ```yaml diff --git a/executors/mongo/mongo.go b/executors/mongo/mongo.go index 0c4bbe60..08491af1 100644 --- a/executors/mongo/mongo.go +++ b/executors/mongo/mongo.go @@ -7,12 +7,14 @@ import ( "io" "os" "path" + "strings" "github.com/mitchellh/mapstructure" "github.com/ovh/venom" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" + "gopkg.in/yaml.v3" ) const Name = "mongo" @@ -49,6 +51,69 @@ func (e Executor) Run(ctx context.Context, step venom.TestStep) (any, error) { for i, action := range e.Actions { actionType := fmt.Sprintf("%v", action["type"]) switch actionType { + case "loadFixtures": + var loadFixturesAction LoadFixturesAction + if err := mapstructure.Decode(action, &loadFixturesAction); err != nil { + return nil, err + } + + if loadFixturesAction.Folder == "" { + return nil, fmt.Errorf("folder is required") + } + + // First, drop the existing collections in the database to start clean + collections, err := mongoClient.Database(e.Database).ListCollectionNames(ctx, bson.M{}) + if err != nil { + return nil, fmt.Errorf("failed to list collections: %w", err) + } + + for _, collection := range collections { + if strings.HasPrefix(collection, "system.") { + continue + } + + if err := mongoClient.Database(e.Database).Collection(collection).Drop(ctx); err != nil { + return nil, fmt.Errorf("failed to drop collection %s: %w", collection, err) + } + } + + dirEntries, err := os.ReadDir(path.Join(venom.StringVarFromCtx(ctx, "venom.testsuite.workdir"), loadFixturesAction.Folder)) + if err != nil { + return nil, err + } + + fixtures := make([]string, 0, len(dirEntries)) + for _, file := range dirEntries { + if file.IsDir() { + continue + } + + extension := path.Ext(file.Name()) + if extension != ".yaml" && extension != ".yml" { + continue + } + fixtures = append(fixtures, file.Name()) + } + + for _, fixture := range fixtures { + collectionName := strings.TrimSuffix(fixture, path.Ext(fixture)) + filePath := path.Join(venom.StringVarFromCtx(ctx, "venom.testsuite.workdir"), loadFixturesAction.Folder, fixture) + + file, err := os.Open(filePath) + if err != nil { + return nil, fmt.Errorf("failed to open file %s: %w", filePath, err) + } + + var documents []any + if err := yaml.NewDecoder(file).Decode(&documents); err != nil { + return nil, fmt.Errorf("failed to decode fixture %s: %w", filePath, err) + } + + if _, err := mongoClient.Database(e.Database).Collection(collectionName).InsertMany(ctx, documents); err != nil { + return nil, fmt.Errorf("failed to insert documents in collection %s: %w", collectionName, err) + } + } + case "dropCollection": if err := mongoClient.Database(e.Database).Collection(e.Collection).Drop(ctx); err != nil { return nil, err @@ -290,6 +355,11 @@ type CountAction struct { } } +type LoadFixturesAction struct { + Action + Folder string +} + type InsertAction struct { Action File string diff --git a/go.mod b/go.mod index 82d54713..b8d90f8c 100644 --- a/go.mod +++ b/go.mod @@ -50,6 +50,7 @@ require ( golang.org/x/net v0.8.0 google.golang.org/grpc v1.53.0 gopkg.in/yaml.v2 v2.4.0 + gopkg.in/yaml.v3 v3.0.1 modernc.org/sqlite v1.21.0 ) @@ -101,7 +102,6 @@ require ( google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect google.golang.org/protobuf v1.29.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect lukechampine.com/uint128 v1.2.0 // indirect modernc.org/cc/v3 v3.40.0 // indirect modernc.org/ccgo/v3 v3.16.13 // indirect diff --git a/tests/mongo.yml b/tests/mongo.yml index e9aed43b..cc429587 100644 --- a/tests/mongo.yml +++ b/tests/mongo.yml @@ -5,6 +5,50 @@ vars: mongo_collection: cards testcases: + - name: Load fixtures + steps: + - type: mongo + uri: "{{.mongo_uri}}" + database: "{{.mongo_database}}" + actions: + - type: loadFixtures + folder: mongo/fixturesA + + - type: mongo + uri: "{{.mongo_uri}}" + database: "{{.mongo_database}}" + collection: cards + actions: + - type: count + assertions: + - result.actions.actions0.count ShouldEqual 3 + + - type: mongo + uri: "{{.mongo_uri}}" + database: "{{.mongo_database}}" + actions: + - type: loadFixtures + folder: mongo/fixturesB + + # Ensure fixturesA have been dropped + - type: mongo + uri: "{{.mongo_uri}}" + database: "{{.mongo_database}}" + collection: cards + actions: + - type: count + assertions: + - result.actions.actions0.count ShouldEqual 0 + + - type: mongo + uri: "{{.mongo_uri}}" + database: "{{.mongo_database}}" + collection: pokemons + actions: + - type: count + assertions: + - result.actions.actions0.count ShouldEqual 3 + - name: Reset collection steps: - type: mongo diff --git a/tests/mongo/fixturesA/cards.yml b/tests/mongo/fixturesA/cards.yml new file mode 100644 index 00000000..19adfab6 --- /dev/null +++ b/tests/mongo/fixturesA/cards.yml @@ -0,0 +1,8 @@ +- suit: clubs + value: jack + +- suit: clubs + value: queen + +- suit: clubs + value: king \ No newline at end of file diff --git a/tests/mongo/fixturesB/pokemons.yml b/tests/mongo/fixturesB/pokemons.yml new file mode 100644 index 00000000..b3023c08 --- /dev/null +++ b/tests/mongo/fixturesB/pokemons.yml @@ -0,0 +1,12 @@ +- name: Bulbasaur + types: + - grass + - poison + +- name: Charmander + types: + - fire + +- name: Squirtle + types: + - water \ No newline at end of file