github.com/mailru/activerecord@v1.12.2/internal/app/argen_b_test.go (about)

     1  package app_test
     2  
     3  import (
     4  	"context"
     5  	"encoding/binary"
     6  	"errors"
     7  	"fmt"
     8  	"os"
     9  	"os/exec"
    10  	"path/filepath"
    11  	"strconv"
    12  	"testing"
    13  
    14  	"github.com/mailru/activerecord/internal/app"
    15  	"github.com/mailru/activerecord/internal/pkg/ds"
    16  	"github.com/mailru/activerecord/internal/pkg/testutil"
    17  	"github.com/mailru/activerecord/pkg/octopus"
    18  )
    19  
    20  func TestInit(t *testing.T) {
    21  	tempDirs := testutil.InitTmps()
    22  	defer tempDirs.Defer()
    23  
    24  	type args struct {
    25  		ctx     context.Context
    26  		appInfo ds.AppInfo
    27  		dirFlag uint32
    28  		modName string
    29  	}
    30  	tests := []struct {
    31  		name    string
    32  		args    args
    33  		wantNil bool
    34  		wantErr bool
    35  	}{
    36  		{
    37  			name: "Non existing src and dst dirs",
    38  			args: args{
    39  				ctx:     context.Background(),
    40  				appInfo: testutil.TestAppInfo,
    41  				dirFlag: testutil.NonExistsSrcDir | testutil.NonExistsDstDir,
    42  				modName: "github.com/mailru/activerecord",
    43  			},
    44  			wantNil: true,
    45  			wantErr: true,
    46  		},
    47  		{
    48  			name: "non existing dst dir",
    49  			args: args{
    50  				ctx:     context.Background(),
    51  				appInfo: testutil.TestAppInfo,
    52  				dirFlag: testutil.NonExistsDstDir,
    53  			},
    54  			wantNil: false,
    55  			wantErr: false,
    56  		},
    57  		{
    58  			name: "non existing src dir",
    59  			args: args{
    60  				ctx:     context.Background(),
    61  				appInfo: testutil.TestAppInfo,
    62  				dirFlag: testutil.NonExistsSrcDir,
    63  			},
    64  			wantNil: true,
    65  			wantErr: true,
    66  		},
    67  		{
    68  			name: "empty src dir",
    69  			args: args{
    70  				ctx:     context.Background(),
    71  				appInfo: testutil.TestAppInfo,
    72  				dirFlag: testutil.EmptyDstDir,
    73  			},
    74  			wantNil: false,
    75  			wantErr: false,
    76  		},
    77  	}
    78  	for _, tt := range tests {
    79  		t.Run(tt.name, func(t *testing.T) {
    80  			srcDir, dstDir, err := tempDirs.CreateDirs(tt.args.dirFlag)
    81  			if err != nil {
    82  				t.Errorf("can't prepare temp dirs: %s", err)
    83  				return
    84  			}
    85  
    86  			got, err := app.Init(tt.args.ctx, &tt.args.appInfo, srcDir, dstDir, "", tt.args.modName)
    87  			if (err != nil) != tt.wantErr {
    88  				t.Errorf("Init() error = %v, res = %+v, wantErr %v", err, got, tt.wantErr)
    89  				return
    90  			}
    91  			if (got == nil) != tt.wantNil {
    92  				t.Errorf("Init() = %+v, wantNil %v", got, tt.wantNil)
    93  				return
    94  			}
    95  		})
    96  	}
    97  }
    98  
    99  func TestArGen_Run(t *testing.T) {
   100  	tempDirs := testutil.InitTmps()
   101  	defer tempDirs.Defer()
   102  
   103  	srcRoot, errTmpDir := tempDirs.AddTempDir()
   104  	if errTmpDir != nil {
   105  		t.Errorf("can't prepare root tmp dir: %s", errTmpDir)
   106  		return
   107  	}
   108  
   109  	src := filepath.Join(srcRoot, "model/repository/decl")
   110  	dst := filepath.Join(srcRoot, "model/repository/argen")
   111  
   112  	if err := os.MkdirAll(src, 0700); err != nil {
   113  		t.Errorf("can't prepare test tmp dir: %s", err)
   114  		return
   115  	}
   116  
   117  	textTestPkg := `package repository
   118  
   119  	//ar:serverHost:127.0.0.1;serverPort:11111;serverTimeout:500
   120  	//ar:namespace:2
   121  	//ar:backend:octopus
   122  	type FieldsFoo struct {
   123  		Field1    int  ` + "`" + `ar:"size:5"` + "`" + `
   124  		Field2    string  ` + "`" + `ar:"size:5"` + "`" + `
   125  	}
   126  
   127  	type (
   128  		IndexesFoo struct {
   129  			Field1Field2 bool ` + "`" + `ar:"fields:Field1,Field2;primary_key"` + "`" + `
   130  		}
   131  		IndexPartsFoo struct {
   132  			Field1Part bool ` + "`" + `ar:"index:Field1Field2;fieldnum:1;selector:SelectByField1"` + "`" + `
   133  		}
   134  	)
   135  
   136  	`
   137  	fieldValue := []byte{0x0A, 0x00, 0x00, 0x00}
   138  	fieldValueStr := strconv.FormatUint(uint64(binary.LittleEndian.Uint32(fieldValue)), 10)
   139  	namespace := []byte{0x02, 0x00, 0x00, 0x00}
   140  	insertFlags := []byte{0x03, 0x00, 0x00, 0x00}
   141  	replaceFlags := []byte{0x05, 0x00, 0x00, 0x00}
   142  	insertTupleCardinality := []byte{0x02, 0x00, 0x00, 0x00}
   143  	insertTupleFields := append([]byte{0x04}, fieldValue...) //len + Field1
   144  	insertTupleFields = append(insertTupleFields, []byte{0x00}...)
   145  
   146  	insertReq := append(namespace, insertFlags...)
   147  	insertReq = append(insertReq, insertTupleCardinality...)
   148  	insertReq = append(insertReq, insertTupleFields...)
   149  
   150  	replaceReq := append(namespace, replaceFlags...)
   151  	replaceReq = append(replaceReq, insertTupleCardinality...)
   152  	replaceReq = append(replaceReq, insertTupleFields...)
   153  
   154  	insertMsg := octopus.RequestTypeInsert // Insert or Replace
   155  
   156  	responseSuccess := []byte{0x00, 0x00, 0x00, 0x00}
   157  	responseErrorDuplicate := []byte{0x02, 0x20, 0x00, 0x00}
   158  	insertRespCount := []byte{0x01, 0x00, 0x00, 0x00}
   159  	insertFqTupleSize := []byte{0x09, 0x00, 0x00, 0x00} //sizeof byte
   160  
   161  	successInsertResponse := append(responseSuccess, insertRespCount...)
   162  	successInsertResponse = append(successInsertResponse, insertFqTupleSize...)
   163  	successInsertResponse = append(successInsertResponse, insertTupleCardinality...)
   164  	successInsertResponse = append(successInsertResponse, insertTupleFields...)
   165  
   166  	duplicateInsertResponse := append(responseErrorDuplicate, []byte("Duplicate key")...)
   167  
   168  	repositoryName := "foo"
   169  
   170  	if err := os.WriteFile(filepath.Join(src, repositoryName+".go"), []byte(textTestPkg), 0600); err != nil {
   171  		t.Errorf("can't write test file: %s", err)
   172  		return
   173  	}
   174  
   175  	emptySrc, emptyDst, err := tempDirs.CreateDirs(testutil.NonExistsDstDir)
   176  	if err != nil {
   177  		t.Errorf("can't initialize dirs: %s", err)
   178  		return
   179  	}
   180  
   181  	type argsRun struct {
   182  		testGoMain string
   183  		fixtures   []octopus.FixtureType
   184  	}
   185  
   186  	type initArgs struct {
   187  		ctx        context.Context
   188  		appInfo    ds.AppInfo
   189  		srcDir     string
   190  		dstDir     string
   191  		dstFixture string
   192  		root       string
   193  		modName    string
   194  	}
   195  
   196  	tests := []struct {
   197  		name     string
   198  		initArgs initArgs
   199  		runArgs  []argsRun
   200  		wantErr  bool
   201  	}{
   202  		{
   203  			name: "run on empty src dir",
   204  			initArgs: initArgs{
   205  				ctx:     context.Background(),
   206  				appInfo: testutil.TestAppInfo,
   207  				srcDir:  emptySrc,
   208  				dstDir:  emptyDst,
   209  				modName: "github.com/mailru/activerecord",
   210  			},
   211  			wantErr: false,
   212  		},
   213  		{
   214  			name: "simplePkg full cycle",
   215  			initArgs: initArgs{
   216  				ctx:     context.Background(),
   217  				appInfo: testutil.TestAppInfo,
   218  				srcDir:  src,
   219  				dstDir:  dst,
   220  				root:    srcRoot,
   221  			},
   222  			wantErr: false,
   223  			runArgs: []argsRun{
   224  				{
   225  					testGoMain: `fooRepo := ` + repositoryName + `.New(ctx)
   226  					fooRepo.SetField1(` + fieldValueStr + `)
   227  						err := fooRepo.Insert(ctx)
   228  						if err != nil {
   229  							log.Fatal(err)
   230  						}
   231  						err = fooRepo.Replace(ctx)
   232  						if err != nil {
   233  							log.Fatal(err)
   234  						}`,
   235  					fixtures: []octopus.FixtureType{
   236  						octopus.CreateFixture(1, uint8(insertMsg), insertReq, successInsertResponse, nil),
   237  						octopus.CreateFixture(2, uint8(insertMsg), replaceReq, successInsertResponse, nil),
   238  					},
   239  				},
   240  				{
   241  					testGoMain: `fooRepo := ` + repositoryName + `.New(ctx)
   242  					fooRepo.SetField1(` + fieldValueStr + `)
   243  						err := fooRepo.Insert(ctx)
   244  						if err == nil {
   245  							log.Fatal("Error test duplicate")
   246  						}`,
   247  					fixtures: []octopus.FixtureType{
   248  						octopus.CreateFixture(1, uint8(insertMsg), insertReq, duplicateInsertResponse, nil),
   249  					},
   250  				},
   251  			},
   252  		},
   253  	}
   254  
   255  	testModuleName := "github.com/foo/bar/baz/test.git"
   256  	srcPath := testutil.GetPathToSrc()
   257  
   258  	for _, tt := range tests {
   259  		t.Run(tt.name, func(t *testing.T) {
   260  			got, err := app.Init(tt.initArgs.ctx, &tt.initArgs.appInfo, tt.initArgs.srcDir, tt.initArgs.dstDir, tt.initArgs.dstFixture, tt.initArgs.modName)
   261  			if (err != nil) != tt.wantErr {
   262  				t.Errorf("ArGen.Init() error = %v, wantErr %v", err, tt.wantErr)
   263  				return
   264  			}
   265  
   266  			if err := got.Run(); (err != nil) != tt.wantErr {
   267  				t.Errorf("ArGen.Run() error = %v, wantErr %v", err, tt.wantErr)
   268  				return
   269  			}
   270  
   271  			if tt.runArgs != nil && len(tt.runArgs) > 0 {
   272  				gomod := `module ` + testModuleName + `
   273  
   274  go 1.19
   275  
   276  require (
   277  	github.com/mailru/activerecord v1.5.4
   278  )
   279  
   280  replace github.com/mailru/activerecord => ` + srcPath
   281  
   282  				modFile := filepath.Join(tt.initArgs.root, "/go.mod")
   283  				if err := os.WriteFile(modFile, []byte(gomod), 0600); err != nil {
   284  					t.Fatalf(fmt.Sprintf("can't write test script: %s", err))
   285  					return
   286  				}
   287  
   288  				oms, err := octopus.InitMockServer(octopus.WithHost("127.0.0.1", "11111"))
   289  				if err != nil {
   290  					t.Fatalf(fmt.Sprintf("can't init octopus server: %s", err))
   291  					return
   292  				}
   293  
   294  				err = oms.Start()
   295  				if err != nil {
   296  					t.Fatalf("Error start octopusMock %s", err)
   297  					return
   298  				}
   299  
   300  				defer func() {
   301  					errStop := oms.Stop()
   302  					if errStop != nil {
   303  						t.Fatalf("Error stop octopusMock %s", errStop)
   304  					}
   305  				}()
   306  
   307  				for _, gr := range tt.runArgs {
   308  					main := `package main
   309  
   310  import (
   311  	"context"
   312  	"fmt"
   313  	"log"
   314  
   315  	"github.com/mailru/activerecord/pkg/activerecord"
   316  	"` + testModuleName + `/model/repository/argen/` + repositoryName + `"
   317  )
   318  
   319  func main() {
   320  	ctx := context.Background()
   321  	log.Printf("Start")
   322  	activerecord.InitActiveRecord(
   323  		activerecord.WithConfig(activerecord.NewDefaultConfigFromMap(map[string]interface{}{
   324  			"arcfg/master":   "127.0.0.1:11111",
   325  			"arcfg/replica":  "127.0.0.1:11111",
   326  		})),
   327  )
   328  	` + gr.testGoMain + `
   329  	//activerecord.ConnectionCacher().CloseConnection(ctx)
   330  	fmt.Print("OK")
   331  }
   332  `
   333  					runFile := filepath.Join(tt.initArgs.root, "/main.go")
   334  					if err = os.WriteFile(runFile, []byte(main), 0600); err != nil {
   335  						t.Fatalf(fmt.Sprintf("can't write test script: %s", err))
   336  						return
   337  					}
   338  
   339  					oms.SetFixtures(gr.fixtures)
   340  
   341  					if _, err = os.Stat(filepath.Join(tt.initArgs.root, "model/repository/argen/foo")); errors.Is(err, os.ErrNotExist) {
   342  						t.Error("repository file not generated or has invalid name")
   343  						return
   344  					}
   345  
   346  					cmd := exec.Command("go", "mod", "tidy")
   347  					cmd.Dir = tt.initArgs.root
   348  					out, err := cmd.CombinedOutput()
   349  					if err != nil {
   350  						t.Fatalf("error run tidy:\n - out: %s\n - err: %s", out, err)
   351  					}
   352  
   353  					cmd = exec.Command("go", "run", runFile)
   354  					cmd.Dir = tt.initArgs.root
   355  					out, err = cmd.Output()
   356  
   357  					if err != nil {
   358  						t.Fatalf("Error exec generated model: %s", err.(*exec.ExitError).Stderr)
   359  					}
   360  
   361  					if string(out) != "OK" {
   362  						t.Fatalf("Exec generated model has bad stdout: %s", out)
   363  					}
   364  				}
   365  			}
   366  		})
   367  	}
   368  }