repogen/internal/mongo/generator_test.go
2023-04-19 19:42:08 +07:00

391 lines
9.5 KiB
Go

package mongo_test
import (
"errors"
"reflect"
"testing"
"github.com/sunboyy/repogen/internal/code"
"github.com/sunboyy/repogen/internal/codegen"
"github.com/sunboyy/repogen/internal/mongo"
"github.com/sunboyy/repogen/internal/spec"
)
var (
idField = code.StructField{
Name: "ID",
Type: code.ExternalType{PackageAlias: "primitive", Name: "ObjectID"},
Tag: `bson:"_id,omitempty"`,
}
genderField = code.StructField{
Name: "Gender",
Type: code.SimpleType("Gender"),
Tag: `bson:"gender"`,
}
ageField = code.StructField{
Name: "Age",
Type: code.TypeInt,
Tag: `bson:"age"`,
}
nameField = code.StructField{
Name: "Name",
Type: code.SimpleType("NameModel"),
Tag: `bson:"name"`,
}
referrerField = code.StructField{
Name: "Referrer",
Type: code.PointerType{ContainedType: code.SimpleType("UserModel")},
Tag: `bson:"referrer"`,
}
consentHistoryField = code.StructField{
Name: "ConsentHistory",
Type: code.ArrayType{ContainedType: code.SimpleType("ConsentHistory")},
Tag: `bson:"consent_history"`,
}
enabledField = code.StructField{
Name: "Enabled",
Type: code.TypeBool,
Tag: `bson:"enabled"`,
}
accessTokenField = code.StructField{
Name: "AccessToken",
Type: code.TypeString,
}
firstNameField = code.StructField{
Name: "First",
Type: code.TypeString,
Tag: `bson:"first"`,
}
)
var userModel = code.Struct{
Name: "UserModel",
Fields: code.StructFields{
idField,
code.StructField{
Name: "Username",
Type: code.TypeString,
Tag: `bson:"username"`,
},
genderField,
ageField,
nameField,
referrerField,
consentHistoryField,
enabledField,
accessTokenField,
},
}
func TestImports(t *testing.T) {
generator := mongo.NewGenerator(userModel, "UserRepository")
expected := [][]code.Import{
{
{Path: "context"},
},
{
{Path: "go.mongodb.org/mongo-driver/bson"},
{Path: "go.mongodb.org/mongo-driver/bson/primitive"},
{Path: "go.mongodb.org/mongo-driver/mongo"},
{Path: "go.mongodb.org/mongo-driver/mongo/options"},
},
}
actual := generator.Imports()
if !reflect.DeepEqual(expected, actual) {
t.Errorf("incorrect imports: expected %+v, got %+v", expected, actual)
}
}
func TestGenerateStruct(t *testing.T) {
generator := mongo.NewGenerator(userModel, "UserRepository")
expected := codegen.StructBuilder{
Name: "UserRepositoryMongo",
Fields: []code.StructField{
{
Name: "collection",
Type: code.PointerType{
ContainedType: code.ExternalType{
PackageAlias: "mongo",
Name: "Collection",
},
},
},
},
}
actual := generator.GenerateStruct()
if expected.Name != actual.Name {
t.Errorf(
"incorrect struct name: expected %s, got %s",
expected.Name,
actual.Name,
)
}
if !reflect.DeepEqual(expected.Fields, actual.Fields) {
t.Errorf(
"incorrect struct fields: expected %+v, got %+v",
expected.Fields,
actual.Fields,
)
}
}
func TestGenerateConstructor(t *testing.T) {
generator := mongo.NewGenerator(userModel, "UserRepository")
expected := codegen.FunctionBuilder{
Name: "NewUserRepository",
Params: []code.Param{
{
Name: "collection",
Type: code.PointerType{
ContainedType: code.ExternalType{
PackageAlias: "mongo",
Name: "Collection",
},
},
},
},
Returns: []code.Type{
code.SimpleType("UserRepository"),
},
Body: codegen.FunctionBody{
codegen.ReturnStatement{
codegen.StructStatement{
Type: "&UserRepositoryMongo",
Pairs: []codegen.StructFieldPair{{
Key: "collection",
Value: codegen.Identifier("collection"),
}},
},
},
},
}
actual, err := generator.GenerateConstructor()
if err != nil {
t.Fatal(err)
}
if expected.Name != actual.Name {
t.Errorf(
"incorrect function name: expected %s, got %s",
expected.Name,
actual.Name,
)
}
if !reflect.DeepEqual(expected.Params, actual.Params) {
t.Errorf(
"incorrect struct params: expected %+v, got %+v",
expected.Params,
actual.Params,
)
}
if !reflect.DeepEqual(expected.Body, actual.Body) {
t.Errorf("incorrect function body: expected %+v got %+v",
expected.Body,
actual.Body,
)
}
}
type GenerateMethodTestCase struct {
Name string
MethodSpec spec.MethodSpec
ExpectedBody string
}
type GenerateMethodInvalidTestCase struct {
Name string
Method spec.MethodSpec
ExpectedError error
}
type StubOperation struct {
}
func (o StubOperation) Name() string {
return "Stub"
}
func TestGenerateMethod_Invalid(t *testing.T) {
testTable := []GenerateMethodInvalidTestCase{
{
Name: "operation not supported",
Method: spec.MethodSpec{
Name: "SearchByID",
Params: []code.Param{
{Type: code.ExternalType{PackageAlias: "context", Name: "Context"}},
{Type: code.ExternalType{PackageAlias: "primitive", Name: "ObjectID"}},
},
Returns: []code.Type{
code.ArrayType{ContainedType: code.PointerType{ContainedType: code.SimpleType("UserModel")}},
code.TypeError,
},
Operation: StubOperation{},
},
ExpectedError: mongo.NewOperationNotSupportedError("Stub"),
},
{
Name: "bson tag not found in query",
Method: spec.MethodSpec{
Name: "FindByAccessToken",
Params: []code.Param{
{Type: code.ExternalType{PackageAlias: "context", Name: "Context"}},
{Type: code.TypeString},
},
Returns: []code.Type{
code.ArrayType{ContainedType: code.PointerType{ContainedType: code.SimpleType("UserModel")}},
code.TypeError,
},
Operation: spec.FindOperation{
Mode: spec.QueryModeOne,
Query: spec.QuerySpec{
Predicates: []spec.Predicate{
{
FieldReference: spec.FieldReference{accessTokenField},
Comparator: spec.ComparatorEqual,
ParamIndex: 1,
},
},
},
},
},
ExpectedError: mongo.NewBsonTagNotFoundError("AccessToken"),
},
{
Name: "bson tag not found in sort",
Method: spec.MethodSpec{
Name: "FindAllOrderByAccessToken",
Params: []code.Param{
{Type: code.ExternalType{PackageAlias: "context", Name: "Context"}},
},
Returns: []code.Type{
code.ArrayType{ContainedType: code.PointerType{ContainedType: code.SimpleType("UserModel")}},
code.TypeError,
},
Operation: spec.FindOperation{
Mode: spec.QueryModeOne,
Sorts: []spec.Sort{
{FieldReference: spec.FieldReference{accessTokenField}, Ordering: spec.OrderingAscending},
},
},
},
ExpectedError: mongo.NewBsonTagNotFoundError("AccessToken"),
},
{
Name: "bson tag not found in update field",
Method: spec.MethodSpec{
Name: "UpdateAccessTokenByID",
Params: []code.Param{
{Type: code.ExternalType{PackageAlias: "context", Name: "Context"}},
{Type: code.TypeString},
{Type: code.ExternalType{PackageAlias: "primitive", Name: "ObjectID"}},
},
Returns: []code.Type{
code.TypeBool,
code.TypeError,
},
Operation: spec.UpdateOperation{
Update: spec.UpdateFields{
spec.UpdateField{
FieldReference: spec.FieldReference{accessTokenField},
ParamIndex: 1,
Operator: spec.UpdateOperatorSet,
},
},
Mode: spec.QueryModeOne,
Query: spec.QuerySpec{
Predicates: []spec.Predicate{
{
FieldReference: spec.FieldReference{idField},
Comparator: spec.ComparatorEqual,
ParamIndex: 2,
},
},
},
},
},
ExpectedError: mongo.NewBsonTagNotFoundError("AccessToken"),
},
{
Name: "update type not supported",
Method: spec.MethodSpec{
Name: "UpdateAgeByID",
Params: []code.Param{
{Type: code.ExternalType{PackageAlias: "context", Name: "Context"}},
{Type: code.TypeInt},
{Type: code.ExternalType{PackageAlias: "primitive", Name: "ObjectID"}},
},
Returns: []code.Type{
code.TypeBool,
code.TypeError,
},
Operation: spec.UpdateOperation{
Update: StubUpdate{},
Mode: spec.QueryModeOne,
Query: spec.QuerySpec{
Predicates: []spec.Predicate{
{
FieldReference: spec.FieldReference{idField},
Comparator: spec.ComparatorEqual,
ParamIndex: 2,
},
},
},
},
},
ExpectedError: mongo.NewUpdateTypeNotSupportedError(StubUpdate{}),
},
{
Name: "update operator not supported",
Method: spec.MethodSpec{
Name: "UpdateConsentHistoryAppendByID",
Params: []code.Param{
{Type: code.ExternalType{PackageAlias: "context", Name: "Context"}},
{Type: code.TypeInt},
{Type: code.ExternalType{PackageAlias: "primitive", Name: "ObjectID"}},
},
Returns: []code.Type{
code.TypeBool,
code.TypeError,
},
Operation: spec.UpdateOperation{
Update: spec.UpdateFields{
spec.UpdateField{
FieldReference: spec.FieldReference{consentHistoryField},
ParamIndex: 1,
Operator: "APPEND",
},
},
Mode: spec.QueryModeOne,
Query: spec.QuerySpec{
Predicates: []spec.Predicate{
{
FieldReference: spec.FieldReference{idField},
Comparator: spec.ComparatorEqual,
ParamIndex: 2,
},
},
},
},
},
ExpectedError: mongo.NewUpdateOperatorNotSupportedError("APPEND"),
},
}
for _, testCase := range testTable {
t.Run(testCase.Name, func(t *testing.T) {
generator := mongo.NewGenerator(userModel, "UserRepository")
_, err := generator.GenerateMethod(testCase.Method)
if !errors.Is(err, testCase.ExpectedError) {
t.Errorf("\nExpected = %+v\nReceived = %+v", testCase.ExpectedError, err)
}
})
}
}