From b614049d037a74814ec088a63ba5ecfcf8a2daa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=20Tr=C3=B6ger?= Date: Wed, 13 Aug 2025 15:50:11 +0200 Subject: [PATCH] added tests for db and user --- .gitignore | 1 + Backend/database.go | 30 ++-------- Backend/endpoints/team.go | 2 +- Backend/endpoints/time.go | 2 +- Backend/endpoints/user-session.go | 2 +- Backend/endpoints/user-settings.go | 2 +- Backend/main.go | 1 - Backend/models/booking.go | 7 ++- Backend/models/db_test.go | 38 +++++++++++++ Backend/models/user.go | 6 +- Backend/models/user_test.go | 56 +++++++++++++++++++ ...{03_sample_data.sql => 02_sample_data.sql} | 2 +- .../{02_create_user.sh => 03_create_user.sh} | 0 Docker/docker-compose.test.yml | 13 +++++ 14 files changed, 126 insertions(+), 36 deletions(-) create mode 100644 Backend/models/db_test.go create mode 100644 Backend/models/user_test.go rename DB/initdb/{03_sample_data.sql => 02_sample_data.sql} (82%) rename DB/initdb/{02_create_user.sh => 03_create_user.sh} (100%) create mode 100644 Docker/docker-compose.test.yml diff --git a/.gitignore b/.gitignore index 11a8757..4e80b98 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,4 @@ DB/pg_data node_modules atlas.hcl +.scannerwork diff --git a/Backend/database.go b/Backend/database.go index f814974..24b7dd6 100644 --- a/Backend/database.go +++ b/Backend/database.go @@ -7,39 +7,17 @@ import ( "fmt" ) -func OpenDatabase() (*sql.DB, error) { +func OpenDatabase() (models.IDatabase, error) { dbHost := helper.GetEnv("POSTGRES_HOST", "localhost") dbName := helper.GetEnv("POSTGRES_DB", "arbeitszeitmessung") dbUser := helper.GetEnv("POSTGRES_API_USER", "api_nutzer") dbPassword := helper.GetEnv("POSTGRES_API_PASS", "password") connStr := fmt.Sprintf("postgres://%s:%s@%s:5432/%s?sslmode=disable&TimeZone=Europe/Berlin", dbUser, dbPassword, dbHost, dbName) - return sql.Open("postgres", connStr) -} - -func GetBookingsByCardID(db *sql.DB, card_id string) ([]models.Booking, error) { - qStr, err := db.Prepare((`SELECT * FROM anwesenheit WHERE card_id = $1`)) + db, err := sql.Open("postgres", connStr) if err != nil { return nil, err } - var bookings []models.Booking - rows, err := qStr.Query(card_id) - if err == sql.ErrNoRows { - return bookings, err - } - if err != nil { - return nil, err - } - defer rows.Close() - for rows.Next() { - var booking models.Booking - if err := rows.Scan(&booking.CounterId, &booking.Timestamp, &booking.CardUID, &booking.GeraetID, &booking.CheckInOut); err != nil { - return bookings, err - } - bookings = append(bookings, booking) - } - if err = rows.Err(); err != nil { - return bookings, err - } - return bookings, nil + defer db.Close() + return db, err } diff --git a/Backend/endpoints/team.go b/Backend/endpoints/team.go index 3526085..b93585e 100644 --- a/Backend/endpoints/team.go +++ b/Backend/endpoints/team.go @@ -33,7 +33,7 @@ func submitReport(w http.ResponseWriter, r *http.Request) { userPN, _ := strconv.Atoi(r.FormValue("user")) _weekTs := r.FormValue("week") weekTs, err := time.Parse(time.DateOnly, _weekTs) - user, err := (*models.User).GetByPersonalNummer(nil, userPN) + user, err := models.GetUserByPersonalNr(userPN) workWeek := (*models.WorkWeek).GetWeek(nil, user, weekTs, false) if err != nil { diff --git a/Backend/endpoints/time.go b/Backend/endpoints/time.go index eef7537..0172b4c 100644 --- a/Backend/endpoints/time.go +++ b/Backend/endpoints/time.go @@ -178,7 +178,7 @@ func getBookingsAPI(w http.ResponseWriter, r *http.Request) { return } - user, err := (*models.User).GetByPersonalNummer(nil, user_pn) + user, err := models.GetUserByPersonalNr(user_pn) if err != nil { log.Println("No user found with the given personal number!") http.Error(w, "No user found", http.StatusNotFound) diff --git a/Backend/endpoints/user-session.go b/Backend/endpoints/user-session.go index 9b9084c..ffed8b9 100644 --- a/Backend/endpoints/user-session.go +++ b/Backend/endpoints/user-session.go @@ -53,7 +53,7 @@ func loginUser(w http.ResponseWriter, r *http.Request) { return } - user, err := (*models.User).GetByPersonalNummer(nil, personal_nummer) + user, err := models.GetUserByPersonalNr(personal_nummer) if err != nil { log.Println("No user found under this personal number!") http.Error(w, "No user found!", http.StatusNotFound) diff --git a/Backend/endpoints/user-settings.go b/Backend/endpoints/user-settings.go index d8509a0..889267d 100644 --- a/Backend/endpoints/user-settings.go +++ b/Backend/endpoints/user-settings.go @@ -21,7 +21,7 @@ func changePassword(w http.ResponseWriter, r *http.Request) { showUserPage(w, r, http.StatusBadRequest) return } - user, err := (*models.User).GetByPersonalNummer(nil, Session.GetInt(r.Context(), "user")) + user, err := models.GetUserByPersonalNr(Session.GetInt(r.Context(), "user")) if err != nil { log.Println("Error getting user!", err) showUserPage(w, r, http.StatusBadRequest) diff --git a/Backend/main.go b/Backend/main.go index 3d2aee2..272eb6f 100644 --- a/Backend/main.go +++ b/Backend/main.go @@ -35,7 +35,6 @@ func main() { if err != nil { log.Fatal(err) } - defer models.DB.Close() fs := http.FileServer(http.Dir("./static")) endpoints.CreateSessionManager(24 * time.Hour) diff --git a/Backend/models/booking.go b/Backend/models/booking.go index 9265808..a739f5b 100644 --- a/Backend/models/booking.go +++ b/Backend/models/booking.go @@ -30,7 +30,12 @@ type Booking struct { CounterId int `json:"counter_id"` } -var DB *sql.DB +type IDatabase interface { + Prepare(query string) (*sql.Stmt, error) + Exec(query string, args ...any) (sql.Result, error) +} + +var DB IDatabase func (b *Booking) New(card_uid string, geraet_id int16, check_in_out int16) Booking { return Booking{ diff --git a/Backend/models/db_test.go b/Backend/models/db_test.go new file mode 100644 index 0000000..21d25f5 --- /dev/null +++ b/Backend/models/db_test.go @@ -0,0 +1,38 @@ +package models_test + +import ( + "arbeitszeitmessung/models" + "database/sql" + "testing" + + _ "github.com/lib/pq" +) + +type DBFixture struct { + Database models.IDatabase + TX *sql.Tx +} + +func SetupDBFixture(t *testing.T) *DBFixture { + t.Helper() + + db, err := sql.Open("postgres", "postgres://postgres:password@localhost:5433/arbeitszeitmessung?sslmode=disable") + if err != nil { + t.Fatalf("failed to connect to database: %v", err) + } + + tx, err := db.Begin() + if err != nil { + t.Fatalf("Failed to start transaction: %v", err) + } + + t.Cleanup(func() { + tx.Rollback() + db.Close() + }) + + return &DBFixture{ + Database: tx, + TX: tx, + } +} diff --git a/Backend/models/user.go b/Backend/models/user.go index 2f181a2..5bd9ded 100644 --- a/Backend/models/user.go +++ b/Backend/models/user.go @@ -24,13 +24,13 @@ func (u *User) GetUserFromSession(Session *scs.SessionManager, ctx context.Conte var user User var err error if helper.GetEnv("GO_ENV", "production") == "debug" { - user, err = (*User).GetByPersonalNummer(nil, 123) + user, err = GetUserByPersonalNr(123) } else { if !Session.Exists(ctx, "user") { log.Println("No user in session storage!") return user, errors.New("No user in session storage!") } - user, err = (*User).GetByPersonalNummer(nil, Session.GetInt(ctx, "user")) + user, err = GetUserByPersonalNr(Session.GetInt(ctx, "user")) } if err != nil { log.Println("Cannot get user from session!") @@ -95,7 +95,7 @@ func (u *User) CheckOut() error { return nil } -func (u *User) GetByPersonalNummer(personalNummer int) (User, error) { +func GetUserByPersonalNr(personalNummer int) (User, error) { var user User qStr, err := DB.Prepare((`SELECT personal_nummer, card_uid, vorname, nachname, arbeitszeit_per_tag FROM s_personal_daten WHERE personal_nummer = $1;`)) diff --git a/Backend/models/user_test.go b/Backend/models/user_test.go new file mode 100644 index 0000000..dd9ccf4 --- /dev/null +++ b/Backend/models/user_test.go @@ -0,0 +1,56 @@ +package models_test + +import ( + "arbeitszeitmessung/models" + "database/sql" + "testing" +) + +var testUser models.User = models.User{Vorname: "Kim", Name: "Mustermensch", PersonalNummer: 456, CardUID: "aaaa-aaaa", ArbeitszeitPerTag: 8} + +func SetupUserFixture(t *testing.T, db models.IDatabase) { + t.Helper() + db.Exec(`INSERT INTO "s_personal_daten" ("personal_nummer", "aktiv_beschaeftigt", "vorname", "nachname", "geburtsdatum", "plz", "adresse", "geschlecht", "card_uid", "hauptbeschaeftigungs_ort", "arbeitszeit_per_tag", "arbeitszeit_min_start", "arbeitszeit_max_ende", "vorgesetzter_pers_nr") VALUES +(456, 't', 'Kim', 'Mustermensch', '2003-02-01', '08963', 'Altenburger Str. 44A', 1, 'aaaa-aaaa', 1, 8, '07:00:00', '20:00:00', 0);`) +} + +func TestGetUserByPersonalNr(t *testing.T) { + tc := SetupDBFixture(t) + SetupUserFixture(t, tc.Database) + + models.DB = tc.Database + + user, err := models.GetUserByPersonalNr(testUser.PersonalNummer) + if err != nil { + t.Fatal(err) + } + if user != testUser { + t.Error("Retrieved user not the same as testUser!") + } + + _, err = models.GetUserByPersonalNr(000) + if err != sql.ErrNoRows { + t.Error("Wrong error handling, when retrieving wrong personalnummer") + } +} + +func TestCheckAnwesenheit(t *testing.T) { + tc := SetupDBFixture(t) + models.DB = tc.Database + SetupUserFixture(t, tc.Database) + + var actual bool + + if actual = testUser.CheckAnwesenheit(); actual != false { + t.Errorf("Checkabwesenheit with no booking should be false but is %t", actual) + } + tc.Database.Exec("INSERT INTO anwesenheit (timestamp, card_uid, check_in_out, geraet_id) VALUES (NOW() - INTERVAL '2 hour', 'aaaa-aaaa', 1, 1);") + if actual = testUser.CheckAnwesenheit(); actual != true { + t.Errorf("Checkabwesenheit with 'kommen' booking should be true but is %t", actual) + } + + tc.Database.Exec("INSERT INTO anwesenheit (timestamp, card_uid, check_in_out, geraet_id) VALUES (NOW() - INTERVAL '1 hour', 'aaaa-aaaa', 2, 1);") + if actual = testUser.CheckAnwesenheit(); actual != false { + t.Errorf("Checkabwesenheit with 'gehen' booking should be false but is %t", actual) + } +} diff --git a/DB/initdb/03_sample_data.sql b/DB/initdb/02_sample_data.sql similarity index 82% rename from DB/initdb/03_sample_data.sql rename to DB/initdb/02_sample_data.sql index 6d84496..44c2cb6 100644 --- a/DB/initdb/03_sample_data.sql +++ b/DB/initdb/02_sample_data.sql @@ -1,5 +1,5 @@ INSERT INTO "s_personal_daten" ("personal_nummer", "aktiv_beschaeftigt", "vorname", "nachname", "geburtsdatum", "plz", "adresse", "geschlecht", "card_uid", "hauptbeschaeftigungs_ort", "arbeitszeit_per_tag", "arbeitszeit_min_start", "arbeitszeit_max_ende", "vorgesetzter_pers_nr") VALUES -(123, 't', 'Max', 'Mustermann', '2003-02-01', '08963', 'Altenburger Str. 44A', 1, 'acde-edca', 1, 7.5, '07:00:00', '20:00:00', 0); +(123, 't', 'Kim', 'Mustermensch', '2003-02-01', '08963', 'Altenburger Str. 44A', 1, 'aaaa-aaaa', 1, 8, '07:00:00', '20:00:00', 0); INSERT INTO "user_password" ("personal_nummer", "pass_hash") VALUES (123, crypt('max_pass', gen_salt('bf'))); diff --git a/DB/initdb/02_create_user.sh b/DB/initdb/03_create_user.sh similarity index 100% rename from DB/initdb/02_create_user.sh rename to DB/initdb/03_create_user.sh diff --git a/Docker/docker-compose.test.yml b/Docker/docker-compose.test.yml new file mode 100644 index 0000000..fbfeb50 --- /dev/null +++ b/Docker/docker-compose.test.yml @@ -0,0 +1,13 @@ +name: arbeitszeitmessung-test +services: + db: + image: postgres:16 + restart: unless-stopped + env_file: + - .env.test + environment: + PGDATA: /var/lib/postgresql/data/pg_data + volumes: + - ${POSTGRES_PATH}/initdb:/docker-entrypoint-initdb.d + ports: + - 5433:5432