package server import ( "context" "encoding/json" "fmt" "io/fs" "log" "net/http" "os" "path/filepath" "strconv" "strings" "testing" "time" "git.javil.eu/jacob1123/budgeteer/bcrypt" "git.javil.eu/jacob1123/budgeteer/config" "git.javil.eu/jacob1123/budgeteer/jwt" "git.javil.eu/jacob1123/budgeteer/postgres" "git.javil.eu/jacob1123/budgeteer/web" txdb "github.com/DATA-DOG/go-txdb" ) var cfg = config.Config{ //nolint:gochecknoglobals DatabaseConnection: "postgres://budgeteer:budgeteer@db:5432/budgeteer_test", SessionSecret: "this_is_my_demo_secret_for_unit_tests", } func init() { //nolint:gochecknoinits _, err := postgres.Connect("pgx", cfg.DatabaseConnection) if err != nil { log.Fatalf("failed connecting to DB for migrations: %v", err) } txdb.Register("pgtx", "pgx", cfg.DatabaseConnection) } func TestMain(t *testing.T) { t.Parallel() queries, err := postgres.Connect("pgtx", cfg.DatabaseConnection) if err != nil { t.Errorf("connect to DB: %v", err) } static, err := fs.Sub(web.Static, "dist") if err != nil { panic("couldn't open static files") } tokenVerifier, err := jwt.NewTokenVerifier(cfg.SessionSecret) if err != nil { panic(fmt.Errorf("couldn't create token verifier: %w", err)) } handler := &Handler{ Service: queries, TokenVerifier: tokenVerifier, CredentialsVerifier: &bcrypt.Verifier{}, StaticFS: http.FS(static), } ctx := context.Background() createUserParams := postgres.CreateUserParams{ Email: "test@example.com", Name: "test@example.com", Password: "this is my dumb password", } user, err := handler.Service.CreateUser(ctx, createUserParams) if err != nil { fmt.Println(err) t.Fail() return } budget, err := handler.Service.NewBudget(ctx, "My nice Budget", user.ID) if err != nil { fmt.Println(err) t.Fail() return } handler.DoYNABImport(ctx, t, budget) loc := time.Now().Location() resultDir := "../testdata/production-export/results" files, err := os.ReadDir(resultDir) if err != nil { t.Errorf("could not load results: %s", err) return } for _, file := range files { if file.IsDir() { continue } name := file.Name()[0 : len(file.Name())-5] parts := strings.Split(name, "-") year, _ := strconv.Atoi(parts[1]) month, _ := strconv.Atoi(parts[2]) first := time.Date(year, time.Month(month), 1, 0, 0, 0, 0, loc) switch parts[0] { case "categories": t.Logf("Checking balances for %s", first.Format("2006-01")) testCaseFile := filepath.Join(resultDir, file.Name()) handler.CheckAvailableBalance(ctx, t, testCaseFile, budget, first) } } // check categories // check accounts } type CategoryTestData struct { Available float64 Activity float64 Assigned float64 } func (h Handler) CheckAvailableBalance(ctx context.Context, t *testing.T, testCaseFile string, budget *postgres.Budget, first time.Time) { t.Helper() data, err := h.prepareBudgeting(ctx, *budget, first) if err != nil { t.Errorf("prepare budgeting: %s", err) return } // 2022-01 assert_equal(t, -115170.56, data.AvailableBalance.GetFloat64(), "available balance") assertEqual(t, -110181.600, data.AvailableBalance.GetFloat64(), "available balance") categoryTestDataFile, err := os.Open(testCaseFile) if err != nil { t.Errorf("could not load category test data: %s", err) return } var categoryTestData map[string]CategoryTestData dec := json.NewDecoder(categoryTestDataFile) err = dec.Decode(&categoryTestData) if err != nil { t.Errorf("could not decode category test data: %s", err) return } for categoryName, categoryTestData := range categoryTestData { found := false for _, category := range data.Categories { name := category.Group + " : " + category.Name if name == categoryName { assertEqual(t, categoryTestData.Available, category.Available.GetFloat64(), "available for "+categoryName) assertEqual(t, categoryTestData.Activity, category.Activity.GetFloat64(), "activity for "+categoryName) assertEqual(t, categoryTestData.Assigned, category.Assigned.GetFloat64(), "assigned for "+categoryName) found = true } } if !found { t.Errorf("category " + categoryName + " was not found in result") } } } func assertEqual(t *testing.T, expected, actual float64, message string) { t.Helper() if expected == actual { return } t.Errorf("%s: expected %f, got %f", message, expected, actual) } func (h Handler) DoYNABImport(ctx context.Context, t *testing.T, budget *postgres.Budget) { t.Helper() budgetID := budget.ID ynab, err := postgres.NewYNABImport(ctx, h.Service.Queries, budgetID) if err != nil { fmt.Println(err) t.Fail() return } transactions, err := os.Open("../testdata/production-export/Register.tsv") if err != nil { fmt.Println(err) t.Fail() return } assignments, err := os.Open("../testdata/production-export/Budget.tsv") if err != nil { fmt.Println(err) t.Fail() return } err = ynab.ImportTransactions(ctx, transactions) if err != nil { fmt.Println(err) t.Fail() return } err = ynab.ImportAssignments(ctx, assignments) if err != nil { fmt.Println(err) t.Fail() return } }