From 9bd287c98d99f2d745018b2f611d915c7840d445 Mon Sep 17 00:00:00 2001 From: Reinaldy Rafli Date: Mon, 11 Mar 2024 21:52:46 +0700 Subject: [PATCH] test(backend): fix test for nocodb related --- backend/nocodb/create_table_records.go | 1 + backend/nocodb/nocodb_test.go | 128 ++++++++++++ backend/nocodb/nocodbmock/inmemory_storage.go | 35 +++- backend/nocodb/nocodbmock/nocodbmock.go | 3 +- backend/nocodb/update_table_records.go | 3 +- backend/server_handler_action.go | 2 +- backend/ticketing/store_payment_receipt.go | 2 +- .../ticketing/store_payment_receipt_test.go | 95 +++++++++ backend/ticketing/ticketing.go | 8 +- backend/ticketing/ticketing_test.go | 182 +----------------- backend/ticketing/validate_payment_receipt.go | 4 +- .../validate_payment_receipt_test.go | 78 ++++++++ backend/ticketing/verify_student.go | 2 +- backend/ticketing/verify_student_test.go | 35 ++++ backend/ticketing/verify_ticket.go | 4 +- docker-compose.yml | 27 +-- 16 files changed, 397 insertions(+), 212 deletions(-) create mode 100644 backend/nocodb/nocodb_test.go create mode 100644 backend/ticketing/store_payment_receipt_test.go create mode 100644 backend/ticketing/validate_payment_receipt_test.go create mode 100644 backend/ticketing/verify_student_test.go diff --git a/backend/nocodb/create_table_records.go b/backend/nocodb/create_table_records.go index c7a067e..e0da7ee 100644 --- a/backend/nocodb/create_table_records.go +++ b/backend/nocodb/create_table_records.go @@ -35,6 +35,7 @@ func (c *Client) CreateTableRecords(ctx context.Context, tableId string, records } request.Header.Add("xc-auth", c.apiToken) + request.Header.Add("Content-Type", "application/json") response, err := c.httpClient.Do(request) if err != nil { diff --git a/backend/nocodb/nocodb_test.go b/backend/nocodb/nocodb_test.go new file mode 100644 index 0000000..2a29b8c --- /dev/null +++ b/backend/nocodb/nocodb_test.go @@ -0,0 +1,128 @@ +package nocodb_test + +import ( + "context" + "crypto/rand" + "encoding/base64" + "net/http/httptest" + "os" + "strconv" + "testing" + "time" + + "conf/nocodb" + "conf/nocodb/nocodbmock" + "github.com/rs/zerolog/log" +) + +var client *nocodb.Client +var tableId string + +func TestMain(m *testing.M) { + baseUrl := os.Getenv("NOCODB_BASE_URL") + apiToken := os.Getenv("NOCODB_API_KEY") + tableId = os.Getenv("NOCODB_TABLE_ID") + if tableId == "" { + tableId = "aabbcc" + } + + var err error = nil + var mockServer *httptest.Server = nil + if baseUrl != "" && apiToken != "" { + client, err = nocodb.NewClient(nocodb.ClientOptions{ + ApiToken: apiToken, + BaseUrl: baseUrl, + }) + if err != nil { + log.Fatal().Err(err).Msg("creating nocodb client") + return + } + } else { + mockServer, err = nocodbmock.NewNocoDBMockServer() + if err != nil { + mockServer.Close() + log.Fatal().Err(err).Msg("creating mock server") + return + } + + client, err = nocodb.NewClient(nocodb.ClientOptions{ + ApiToken: "testing", + BaseUrl: mockServer.URL, + HttpClient: mockServer.Client(), + }) + if err != nil { + log.Fatal().Err(err).Msg("creating nocodb client") + return + } + } + + exitCode := m.Run() + + if mockServer != nil { + mockServer.Close() + } + + os.Exit(exitCode) +} + +type testBody struct { + Id int64 `json:"Id,omitempty"` + Title string + Age int + RandomText string +} + +func TestIntegration(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + randomBytes := make([]byte, 16) + rand.Read(randomBytes) + randomText := base64.StdEncoding.EncodeToString(randomBytes) + payload := testBody{ + Title: "John Doe", + Age: 49, + RandomText: randomText, + } + + err := client.CreateTableRecords(ctx, tableId, []any{payload}) + if err != nil { + t.Errorf("unexpected error: %s", err.Error()) + } + + var outPayload []testBody + pageInfo, err := client.ListTableRecords(ctx, tableId, &outPayload, nocodb.ListTableRecordOptions{}) + if err != nil { + t.Errorf("unexpected error: %s", err.Error()) + } + + found := false + foundPayload := testBody{} + for _, out := range outPayload { + if out.RandomText == randomText { + found = true + foundPayload = out + } + } + if !found { + t.Errorf("expecting just inserted entry to be found, got not found") + } + if pageInfo.TotalRows <= 0 { + t.Errorf("expecting pageInfo.TotalRows to be a positive number greater than one, got %d", pageInfo.TotalRows) + } + + err = client.UpdateTableRecords(ctx, tableId, []any{map[string]any{"Id": foundPayload.Id, "Age": 320}}) + if err != nil { + t.Errorf("unexpected error: %s", err.Error()) + } + + var anotherOutPayload testBody + err = client.ReadTableRecords(ctx, tableId, strconv.FormatInt(foundPayload.Id, 10), &anotherOutPayload, nocodb.ReadTableRecordsOptions{}) + if err != nil { + t.Errorf("unexpected error: %s", err.Error()) + } + + if anotherOutPayload.Age != 320 { + t.Errorf("expecting Age to be updated to 320, got %d", anotherOutPayload.Age) + } +} diff --git a/backend/nocodb/nocodbmock/inmemory_storage.go b/backend/nocodb/nocodbmock/inmemory_storage.go index 69bafe2..52b6db4 100644 --- a/backend/nocodb/nocodbmock/inmemory_storage.go +++ b/backend/nocodb/nocodbmock/inmemory_storage.go @@ -37,7 +37,22 @@ func (s *storage) GetByRecordId(tableId string, recordId int64) (record map[stri } for _, record := range records { - if record["Id"] == recordId { + var id int64 + switch v := record["Id"].(type) { + case float64: + id = int64(v) + break + case float32: + id = int64(v) + break + case int: + id = int64(v) + break + case int64: + id = v + break + } + if recordId == id { return record, nil } } @@ -79,7 +94,23 @@ func (s *storage) Update(tableId string, records []map[string]any) (ids []int64, for i := 0; i < len(oldRecords); i++ { // I know that this is O(n^2) but because this is a mock, I don't really care for _, record := range records { - if oldRecords[i]["Id"] == record["Id"] { + var recordId int64 + switch v := record["Id"].(type) { + case float64: + recordId = int64(v) + break + case float32: + recordId = int64(v) + break + case int: + recordId = int64(v) + break + case int64: + recordId = v + break + } + + if oldRecords[i]["Id"] == recordId { // Found one for key, value := range record { oldRecords[i][key] = value diff --git a/backend/nocodb/nocodbmock/nocodbmock.go b/backend/nocodb/nocodbmock/nocodbmock.go index cd58959..9cdc717 100644 --- a/backend/nocodb/nocodbmock/nocodbmock.go +++ b/backend/nocodb/nocodbmock/nocodbmock.go @@ -2,6 +2,7 @@ package nocodbmock import ( "encoding/json" + "errors" "net/http" "net/http/httptest" "strconv" @@ -38,7 +39,7 @@ func NewNocoDBMockServer() (*httptest.Server, error) { } records, err := documentStorage.GetByTableId(tableId) - if err != nil { + if err != nil && !errors.Is(err, errNotFound) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusBadRequest) _ = json.NewEncoder(w).Encode(errorResponse{Message: "BadRequest [ERROR]: " + err.Error()}) diff --git a/backend/nocodb/update_table_records.go b/backend/nocodb/update_table_records.go index 8a9391b..3d0371d 100644 --- a/backend/nocodb/update_table_records.go +++ b/backend/nocodb/update_table_records.go @@ -39,7 +39,8 @@ func (c *Client) UpdateTableRecords(ctx context.Context, tableId string, records } request.Header.Add("xc-auth", c.apiToken) - + request.Header.Add("Content-Type", "application/json") + response, err := c.httpClient.Do(request) if err != nil { return fmt.Errorf("executing http request: %w", err) diff --git a/backend/server_handler_action.go b/backend/server_handler_action.go index 1dac763..f77ecfb 100644 --- a/backend/server_handler_action.go +++ b/backend/server_handler_action.go @@ -94,7 +94,7 @@ func ServerHandlerAction(ctx *cli.Context) error { SmtpPassword: config.Mailer.Password, }) - ticketDomain, err := ticketing.NewTicketDomain(database, bucket, signaturePrivateKey, signaturePublicKey, mailSender) + ticketDomain, err := ticketing.NewTicketDomain(database, bucket, signaturePrivateKey, signaturePublicKey, mailSender, config.Database.TicketingTableId) if err != nil { return fmt.Errorf("creating ticket domain: %w", err) } diff --git a/backend/ticketing/store_payment_receipt.go b/backend/ticketing/store_payment_receipt.go index 69a0c1a..cbb107a 100644 --- a/backend/ticketing/store_payment_receipt.go +++ b/backend/ticketing/store_payment_receipt.go @@ -49,7 +49,7 @@ func (t *TicketDomain) StorePaymentReceipt(ctx context.Context, user user.User, return fmt.Errorf("uploading to bucket storage: %w", err) } - err = t.db.CreateTableRecords(ctx, "TODO: Table id", []any{Ticketing{ + err = t.db.CreateTableRecords(ctx, t.tableId, []any{Ticketing{ Email: user.Email, ReceiptPhotoPath: blobKey, Paid: false, diff --git a/backend/ticketing/store_payment_receipt_test.go b/backend/ticketing/store_payment_receipt_test.go new file mode 100644 index 0000000..52484b2 --- /dev/null +++ b/backend/ticketing/store_payment_receipt_test.go @@ -0,0 +1,95 @@ +package ticketing_test + +import ( + "context" + "crypto/ed25519" + "crypto/rand" + "errors" + "strings" + "testing" + "time" + + "conf/ticketing" + "conf/user" +) + +func TestTicketDomain_StorePaymentReceipt(t *testing.T) { + publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader) + if err != nil { + t.Fatalf("generating new ed25519 key: %s", err.Error()) + return + } + + ticketDomain, err := ticketing.NewTicketDomain(database, bucket, privateKey, publicKey, mailSender, tableId) + if err != nil { + t.Fatalf("creating a ticket domain instance: %s", err.Error()) + } + + userDomain, err := user.NewUserDomain(database, "testing") + if err != nil { + t.Fatalf("creating user domain instance: %s", err.Error()) + } + + t.Run("Invalid photo", func(t *testing.T) { + err := ticketDomain.StorePaymentReceipt(context.Background(), user.User{}, nil, "") + if err == nil { + t.Error("expecting an error, got nil instead") + } + + var validationError *ticketing.ValidationError + if errors.As(err, &validationError) { + if len(validationError.Errors) != 2 { + t.Errorf("expecting two errors, got %d", len(validationError.Errors)) + } + } + }) + + t.Run("Happy scenario", func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) + defer cancel() + + email := "johndoe+happy@example.com" + err := userDomain.CreateParticipant(ctx, user.CreateParticipantRequest{ + Name: "John Doe", + Email: email, + }) + if err != nil { + t.Errorf("unexpected error: %s", err.Error()) + } + + err = ticketDomain.StorePaymentReceipt(ctx, user.User{Email: email}, strings.NewReader("Hello world! This is not a photo. Yet this will be a text file."), "text/plain") + if err != nil { + t.Errorf("unexpected error: %s", err.Error()) + } + }) + + t.Run("Update data if email already exists", func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) + defer cancel() + + email := "johndoe+happy@example.com" + err := userDomain.CreateParticipant(ctx, user.CreateParticipantRequest{ + Name: "John Doe", + Email: email, + }) + if err != nil { + t.Errorf("unexpected error: %s", err.Error()) + } + + user := user.User{ + Email: email, + } + + // First attempt + err = ticketDomain.StorePaymentReceipt(ctx, user, strings.NewReader("Hello world! This is not a photo. Yet this will be a text file."), "text/plain") + if err != nil { + t.Errorf("unexpected error: %s", err.Error()) + } + + // Second attempt, should not return error + err = ticketDomain.StorePaymentReceipt(ctx, user, strings.NewReader("Hello world! This is not a photo. Yet this will be a text file."), "text/plain") + if err != nil { + t.Errorf("unexpected error: %s", err.Error()) + } + }) +} diff --git a/backend/ticketing/ticketing.go b/backend/ticketing/ticketing.go index 0a42be7..8bdb0a4 100644 --- a/backend/ticketing/ticketing.go +++ b/backend/ticketing/ticketing.go @@ -17,13 +17,14 @@ import ( type TicketDomain struct { db *nocodb.Client + tableId string bucket *blob.Bucket privateKey *ed25519.PrivateKey publicKey *ed25519.PublicKey mailer *mailer.Mailer } -func NewTicketDomain(db *nocodb.Client, bucket *blob.Bucket, privateKey ed25519.PrivateKey, publicKey ed25519.PublicKey, mailer *mailer.Mailer) (*TicketDomain, error) { +func NewTicketDomain(db *nocodb.Client, bucket *blob.Bucket, privateKey ed25519.PrivateKey, publicKey ed25519.PublicKey, mailer *mailer.Mailer, tableId string) (*TicketDomain, error) { if db == nil { return nil, fmt.Errorf("db is nil") } @@ -44,12 +45,17 @@ func NewTicketDomain(db *nocodb.Client, bucket *blob.Bucket, privateKey ed25519. return nil, fmt.Errorf("mailer is nil") } + if tableId == "" { + return nil, fmt.Errorf("tableId is nil") + } + return &TicketDomain{ db: db, bucket: bucket, privateKey: &privateKey, publicKey: &publicKey, mailer: mailer, + tableId: tableId, }, nil } diff --git a/backend/ticketing/ticketing_test.go b/backend/ticketing/ticketing_test.go index 7caaec4..109e9d6 100644 --- a/backend/ticketing/ticketing_test.go +++ b/backend/ticketing/ticketing_test.go @@ -3,12 +3,9 @@ package ticketing_test import ( "context" "crypto/ed25519" - "crypto/rand" "database/sql" "encoding/json" - "errors" "os" - "strings" "testing" "time" @@ -16,7 +13,6 @@ import ( "conf/nocodb" "conf/nocodb/nocodbmock" "conf/ticketing" - "conf/user" "github.com/getsentry/sentry-go" "github.com/rs/zerolog/log" "gocloud.dev/blob" @@ -26,6 +22,7 @@ import ( var database *nocodb.Client var bucket *blob.Bucket var mailSender *mailer.Mailer +var tableId = "ticketing" func TestMain(m *testing.M) { tempDir, err := os.MkdirTemp(os.TempDir(), "teknologi-umum-conference") @@ -107,7 +104,7 @@ func TestNewTicketDomain(t *testing.T) { // Group the tests with t.Run(). t.Run("all dependencies set", func(t *testing.T) { - ticketDomain, err := ticketing.NewTicketDomain(db, bucket, privateKey, publicKey, mailSender) + ticketDomain, err := ticketing.NewTicketDomain(db, bucket, privateKey, publicKey, mailSender, "asd") if err != nil { t.Errorf("NewTicketDomain failed: %v", err) } @@ -117,7 +114,7 @@ func TestNewTicketDomain(t *testing.T) { }) t.Run("nil database", func(t *testing.T) { - ticketDomain, err := ticketing.NewTicketDomain(nil, bucket, privateKey, publicKey, mailSender) + ticketDomain, err := ticketing.NewTicketDomain(nil, bucket, privateKey, publicKey, mailSender, "") if err == nil { t.Error("NewTicketDomain did not return error with nil database") } @@ -127,7 +124,7 @@ func TestNewTicketDomain(t *testing.T) { }) t.Run("nil bucket", func(t *testing.T) { - ticketDomain, err := ticketing.NewTicketDomain(db, nil, privateKey, publicKey, mailSender) + ticketDomain, err := ticketing.NewTicketDomain(db, nil, privateKey, publicKey, mailSender, "") if err == nil { t.Error("NewTicketDomain did not return error with nil bucket") } @@ -137,7 +134,7 @@ func TestNewTicketDomain(t *testing.T) { }) t.Run("nil private key", func(t *testing.T) { - ticketDomain, err := ticketing.NewTicketDomain(db, bucket, nil, publicKey, mailSender) + ticketDomain, err := ticketing.NewTicketDomain(db, bucket, nil, publicKey, mailSender, "") if err == nil { t.Error("NewTicketDomain did not return error with nil private key") } @@ -147,7 +144,7 @@ func TestNewTicketDomain(t *testing.T) { }) t.Run("nil public key", func(t *testing.T) { - ticketDomain, err := ticketing.NewTicketDomain(db, bucket, privateKey, nil, mailSender) + ticketDomain, err := ticketing.NewTicketDomain(db, bucket, privateKey, nil, mailSender, "") if err == nil { t.Error("NewTicketDomain did not return error with nil public key") } @@ -157,7 +154,7 @@ func TestNewTicketDomain(t *testing.T) { }) t.Run("nil mailSender", func(t *testing.T) { - ticketDomain, err := ticketing.NewTicketDomain(db, bucket, privateKey, publicKey, nil) + ticketDomain, err := ticketing.NewTicketDomain(db, bucket, privateKey, publicKey, nil, "") if err == nil { t.Error("NewTicketDomain did not return error with nil mailSender") } @@ -167,171 +164,6 @@ func TestNewTicketDomain(t *testing.T) { }) } -func TestTicketDomain_StorePaymentReceipt(t *testing.T) { - publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader) - if err != nil { - t.Fatalf("generating new ed25519 key: %s", err.Error()) - return - } - - ticketDomain, err := ticketing.NewTicketDomain(database, bucket, privateKey, publicKey, mailSender) - if err != nil { - t.Fatalf("creating a ticket domain instance: %s", err.Error()) - } - - userDomain, err := user.NewUserDomain(database, "testing") - if err != nil { - t.Fatalf("creating user domain instance: %s", err.Error()) - } - - t.Run("Invalid photo", func(t *testing.T) { - err := ticketDomain.StorePaymentReceipt(context.Background(), user.User{}, nil, "") - if err == nil { - t.Error("expecting an error, got nil instead") - } - - var validationError *ticketing.ValidationError - if errors.As(err, &validationError) { - if len(validationError.Errors) != 2 { - t.Errorf("expecting two errors, got %d", len(validationError.Errors)) - } - } - }) - - t.Run("Happy scenario", func(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) - defer cancel() - - email := "johndoe+happy@example.com" - err := userDomain.CreateParticipant(ctx, user.CreateParticipantRequest{ - Name: "John Doe", - Email: email, - }) - if err != nil { - t.Errorf("unexpected error: %s", err.Error()) - } - - err = ticketDomain.StorePaymentReceipt(ctx, user.User{Email: email}, strings.NewReader("Hello world! This is not a photo. Yet this will be a text file."), "text/plain") - if err != nil { - t.Errorf("unexpected error: %s", err.Error()) - } - }) - - t.Run("Update data if email already exists", func(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) - defer cancel() - - email := "johndoe+happy@example.com" - err := userDomain.CreateParticipant(ctx, user.CreateParticipantRequest{ - Name: "John Doe", - Email: email, - }) - if err != nil { - t.Errorf("unexpected error: %s", err.Error()) - } - - user := user.User{ - Email: email, - } - - // First attempt - err = ticketDomain.StorePaymentReceipt(ctx, user, strings.NewReader("Hello world! This is not a photo. Yet this will be a text file."), "text/plain") - if err != nil { - t.Errorf("unexpected error: %s", err.Error()) - } - - // Second attempt, should not return error - err = ticketDomain.StorePaymentReceipt(ctx, user, strings.NewReader("Hello world! This is not a photo. Yet this will be a text file."), "text/plain") - if err != nil { - t.Errorf("unexpected error: %s", err.Error()) - } - }) -} - -func TestTicketDomain_ValidatePaymentReceipt(t *testing.T) { - publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader) - if err != nil { - t.Fatalf("generating new ed25519 key: %s", err.Error()) - return - } - - ticketDomain, err := ticketing.NewTicketDomain(database, bucket, privateKey, publicKey, mailSender) - if err != nil { - t.Fatalf("creating a ticket domain instance: %s", err.Error()) - } - - userDomain, err := user.NewUserDomain(database, "testing") - if err != nil { - t.Fatalf("creating user domain instance: %s", err.Error()) - } - - t.Run("Email not found", func(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), time.Minute) - defer cancel() - - _, err := ticketDomain.ValidatePaymentReceipt(ctx, user.User{Email: "not-found@example.com"}) - if err == nil { - t.Error("expecting an error, got nil") - } - - if !errors.Is(err, ticketing.ErrInvalidTicket) { - t.Errorf("expecting an error of ErrInvalidTicket, instead got %s", err.Error()) - } - }) - - t.Run("Happy scenario", func(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), time.Minute) - defer cancel() - - email := "johndoe+happy@example.com" - err := userDomain.CreateParticipant(ctx, user.CreateParticipantRequest{ - Name: "John Doe", - Email: email, - }) - if err != nil { - t.Errorf("unexpected error: %s", err.Error()) - } - - user := user.User{Email: email} - - err = ticketDomain.StorePaymentReceipt(ctx, user, strings.NewReader("Hello world! This is not a photo. Yet this will be a text file."), "text/plain") - if err != nil { - t.Errorf("unexpected error: %s", err.Error()) - } - - sum, err := ticketDomain.ValidatePaymentReceipt(ctx, user) - if err != nil { - t.Errorf("unexpected error: %s", err) - } - - if sum == "" { - t.Error("expecting sum to have value, got empty string") - } - }) -} - -func TestTicketDomain_VerifyIsStudent(t *testing.T) { - publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader) - if err != nil { - t.Fatalf("generating new ed25519 key: %s", err.Error()) - return - } - - ticketDomain, err := ticketing.NewTicketDomain(database, bucket, privateKey, publicKey, mailSender) - if err != nil { - t.Fatalf("creating a ticket domain instance: %s", err.Error()) - } - - t.Run("Happy scenario", func(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), time.Minute) - defer cancel() - - err := ticketDomain.VerifyIsStudent(ctx, user.User{Email: "aji@test.com"}) - if err != nil { - t.Errorf("unexpected error: %s", err.Error()) - } - }) -} func TestNullTicketing_MarshalJSON(t *testing.T) { n := ticketing.NullTicketing{ Id: sql.NullInt64{Valid: true, Int64: 123}, diff --git a/backend/ticketing/validate_payment_receipt.go b/backend/ticketing/validate_payment_receipt.go index 500112f..9ac2c9a 100644 --- a/backend/ticketing/validate_payment_receipt.go +++ b/backend/ticketing/validate_payment_receipt.go @@ -32,7 +32,7 @@ func (t *TicketDomain) ValidatePaymentReceipt(ctx context.Context, user user.Use // Mark payment status as paid on database var rawTicketingResults []Ticketing - _, err := t.db.ListTableRecords(ctx, "TODO: Table Id", &rawTicketingResults, nocodb.ListTableRecordOptions{ + _, err := t.db.ListTableRecords(ctx, t.tableId, &rawTicketingResults, nocodb.ListTableRecordOptions{ Where: fmt.Sprintf("(Email,eq,%s)", user.Email), Sort: []nocodb.Sort{nocodb.SortDescending("CreatedAt")}, Limit: 1, @@ -142,7 +142,7 @@ harap abaikan email ini. Terima kasih!`, return "", fmt.Errorf("sending mail: %w", err) } - err = t.db.UpdateTableRecords(ctx, "TODO: Table Id", []any{NullTicketing{ + err = t.db.UpdateTableRecords(ctx, t.tableId, []any{NullTicketing{ Id: sql.NullInt64{Int64: ticketing.Id, Valid: true}, Paid: sql.NullBool{Bool: true, Valid: true}, SHA256Sum: sql.NullString{String: hex.EncodeToString(sha256Sum), Valid: true}, diff --git a/backend/ticketing/validate_payment_receipt_test.go b/backend/ticketing/validate_payment_receipt_test.go new file mode 100644 index 0000000..bba88b5 --- /dev/null +++ b/backend/ticketing/validate_payment_receipt_test.go @@ -0,0 +1,78 @@ +package ticketing_test + +import ( + "context" + "crypto/ed25519" + "crypto/rand" + "errors" + "strings" + "testing" + "time" + + "conf/ticketing" + "conf/user" +) + +func TestTicketDomain_ValidatePaymentReceipt(t *testing.T) { + publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader) + if err != nil { + t.Fatalf("generating new ed25519 key: %s", err.Error()) + return + } + + ticketDomain, err := ticketing.NewTicketDomain(database, bucket, privateKey, publicKey, mailSender, tableId) + if err != nil { + t.Fatalf("creating a ticket domain instance: %s", err.Error()) + } + + userDomain, err := user.NewUserDomain(database, "testing") + if err != nil { + t.Fatalf("creating user domain instance: %s", err.Error()) + } + + t.Run("Email not found", func(t *testing.T) { + t.Skip("We don't do any parameter checking on mock server") + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + _, err := ticketDomain.ValidatePaymentReceipt(ctx, user.User{Email: "not-found@example.com"}) + if err == nil { + t.Error("expecting an error, got nil") + } + + if !errors.Is(err, ticketing.ErrInvalidTicket) { + t.Errorf("expecting an error of ErrInvalidTicket, instead got %s", err.Error()) + } + }) + + t.Run("Happy scenario", func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + email := "johndoe+happy@example.com" + err := userDomain.CreateParticipant(ctx, user.CreateParticipantRequest{ + Name: "John Doe", + Email: email, + }) + if err != nil { + t.Errorf("unexpected error: %s", err.Error()) + } + + user := user.User{Email: email} + + err = ticketDomain.StorePaymentReceipt(ctx, user, strings.NewReader("Hello world! This is not a photo. Yet this will be a text file."), "text/plain") + if err != nil { + t.Errorf("unexpected error: %s", err.Error()) + } + + sum, err := ticketDomain.ValidatePaymentReceipt(ctx, user) + if err != nil { + t.Errorf("unexpected error: %s", err) + } + + if sum == "" { + t.Error("expecting sum to have value, got empty string") + } + }) +} diff --git a/backend/ticketing/verify_student.go b/backend/ticketing/verify_student.go index 27808a9..57fe9cf 100644 --- a/backend/ticketing/verify_student.go +++ b/backend/ticketing/verify_student.go @@ -16,7 +16,7 @@ func (t *TicketDomain) VerifyIsStudent(ctx context.Context, user user.User) (err defer span.Finish() var rawTicketingResults []Ticketing - _, err = t.db.ListTableRecords(ctx, "TODO: Table Id", &rawTicketingResults, nocodb.ListTableRecordOptions{ + _, err = t.db.ListTableRecords(ctx, t.tableId, &rawTicketingResults, nocodb.ListTableRecordOptions{ Where: fmt.Sprintf("(Email,eq,%s)", user.Email), Sort: []nocodb.Sort{nocodb.SortDescending("CreatedAt")}, Limit: 1, diff --git a/backend/ticketing/verify_student_test.go b/backend/ticketing/verify_student_test.go new file mode 100644 index 0000000..c66394f --- /dev/null +++ b/backend/ticketing/verify_student_test.go @@ -0,0 +1,35 @@ +package ticketing_test + +import ( + "context" + "crypto/ed25519" + "crypto/rand" + "testing" + "time" + + "conf/ticketing" + "conf/user" +) + +func TestTicketDomain_VerifyIsStudent(t *testing.T) { + publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader) + if err != nil { + t.Fatalf("generating new ed25519 key: %s", err.Error()) + return + } + + ticketDomain, err := ticketing.NewTicketDomain(database, bucket, privateKey, publicKey, mailSender, tableId) + if err != nil { + t.Fatalf("creating a ticket domain instance: %s", err.Error()) + } + + t.Run("Happy scenario", func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + err := ticketDomain.VerifyIsStudent(ctx, user.User{Email: "aji@test.com"}) + if err != nil { + t.Errorf("unexpected error: %s", err.Error()) + } + }) +} diff --git a/backend/ticketing/verify_ticket.go b/backend/ticketing/verify_ticket.go index 47c488d..7d44ee0 100644 --- a/backend/ticketing/verify_ticket.go +++ b/backend/ticketing/verify_ticket.go @@ -62,7 +62,7 @@ func (t *TicketDomain) VerifyTicket(ctx context.Context, payload []byte) (ticket // Check the ticket if it's been used before. If it is, return ErrInvalidTicket. Decorate it a bit. var rawTicketingResults []Ticketing - _, err = t.db.ListTableRecords(ctx, "TODO: Table Id", &rawTicketingResults, nocodb.ListTableRecordOptions{ + _, err = t.db.ListTableRecords(ctx, t.tableId, &rawTicketingResults, nocodb.ListTableRecordOptions{ Where: fmt.Sprintf("(Id,eq,%d)~and(Used,eq,false)", ticketId), Sort: []nocodb.Sort{nocodb.SortDescending("CreatedAt")}, Limit: 1, @@ -86,7 +86,7 @@ func (t *TicketDomain) VerifyTicket(ctx context.Context, payload []byte) (ticket } // Mark the ticket as used - err = t.db.UpdateTableRecords(ctx, "TODO: Table Id", []any{NullTicketing{ + err = t.db.UpdateTableRecords(ctx, t.tableId, []any{NullTicketing{ Id: sql.NullInt64{Int64: ticketing.Id, Valid: true}, Used: sql.NullBool{Bool: true, Valid: true}, UpdatedAt: sql.NullTime{Time: time.Now(), Valid: true}, diff --git a/docker-compose.yml b/docker-compose.yml index 6ce544b..5363dff 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -46,38 +46,15 @@ services: options: max-size: 10M - backend-migrate: - build: ./backend - entrypoint: /app/conf-backend migrate up - environment: - DB_HOST: postgres - DB_PORT: 5432 - DB_USER: conference - DB_PASSWORD: VeryStrongPassword - DB_NAME: conference - depends_on: - postgres: - condition: service_healthy - logging: - driver: local - options: - max-size: 10M - backend: build: ./backend ports: - 127.0.0.1:8080:8080 environment: - DB_HOST: postgres - DB_PORT: 5432 - DB_USER: conference - DB_PASSWORD: VeryStrongPassword - DB_NAME: conference + NOCODB_BASE_URL: http://nocodb:8080 PORT: 8080 depends_on: - backend-migrate: - condition: service_completed_successfully - postgres: + nocodb: condition: service_healthy mailcrab: condition: service_started