material_manage_system/internal/repository/user_organization_test.go
2026-10-09 09:47:27 +08:00

141 lines
6.3 KiB
Go

package repository
import (
"encoding/json"
"errors"
"testing"
"github.com/DATA-DOG/go-sqlmock"
"github.com/wmms/wmms/internal/model"
"github.com/wmms/wmms/pkg/database"
)
func TestUserOrganizationReplacement(t *testing.T) {
for _, tc := range []struct {
name string
ids []int64
def string
active int
valid bool
}{
{"deduplicates", []int64{2, 2}, "2", 1, true},
{"disabled", []int64{2}, "2", 0, false},
{"missing", []int64{9}, "9", 0, false},
{"default outside set", []int64{2}, "3", 1, false},
{"nonempty without default", []int64{2}, "null", 1, false},
{"invalid id", []int64{-1}, "null", 0, false},
{"revoke all", []int64{}, "null", 0, true},
{"empty with default", []int64{}, "2", 0, false},
} {
t.Run(tc.name, func(t *testing.T) {
db, mock, _ := sqlmock.New()
defer db.Close()
mock.ExpectBegin()
tx, _ := db.Begin()
if tc.name != "invalid id" {
mock.ExpectQuery("SELECT organization_id, is_default FROM user_organizations").WithArgs(int64(7)).WillReturnRows(sqlmock.NewRows([]string{"organization_id", "is_default"}).AddRow(1, true))
mock.ExpectQuery("SELECT EXISTS").WithArgs(int64(7)).WillReturnRows(sqlmock.NewRows([]string{"admin"}).AddRow(false))
if len(tc.ids) > 0 && tc.name != "default outside set" && tc.name != "nonempty without default" {
mock.ExpectQuery("SELECT id, status FROM organizations").WithArgs(tc.ids[0]).WillReturnRows(func() *sqlmock.Rows {
rows := sqlmock.NewRows([]string{"id", "status"})
if tc.active > 0 {
rows.AddRow(tc.ids[0], "active")
}
return rows
}())
}
}
if tc.valid {
mock.ExpectExec("DELETE FROM user_organizations").WithArgs(int64(7)).WillReturnResult(sqlmock.NewResult(0, 1))
if len(tc.ids) > 0 {
mock.ExpectExec("INSERT INTO user_organizations").WithArgs(int64(7), int64(2), true).WillReturnResult(sqlmock.NewResult(0, 1))
}
mock.ExpectExec("INSERT INTO organization_audit_logs").WithArgs(int64(99), nil, "organization_access_changed", sqlmock.AnyArg(), sqlmock.AnyArg()).WillReturnResult(sqlmock.NewResult(1, 1))
}
err := replaceUserOrganizationsTx(tx, 7, model.UserOrganizationInput{OrganizationIDs: &tc.ids, DefaultOrganizationID: json.RawMessage(tc.def), ActorUserID: 99}, false)
if tc.valid && err != nil {
t.Fatal(err)
}
if !tc.valid && !errors.Is(err, ErrInvalidOrganizations) {
t.Fatalf("want invalid organizations, got %v", err)
}
mock.ExpectRollback()
_ = tx.Rollback()
if err := mock.ExpectationsWereMet(); err != nil {
t.Fatal(err)
}
})
}
}
func TestUserOrganizationOmittedPreservesAccess(t *testing.T) {
db, mock, _ := sqlmock.New()
defer db.Close()
mock.ExpectBegin()
tx, _ := db.Begin()
if err := replaceUserOrganizationsTx(tx, 7, model.UserOrganizationInput{ActorUserID: 99}, false); err != nil {
t.Fatal(err)
}
mock.ExpectRollback()
_ = tx.Rollback()
if err := mock.ExpectationsWereMet(); err != nil {
t.Fatal(err)
}
}
func TestUserOrganizationDefaultChangeAuditsOriginalDefault(t *testing.T) {
db, mock, err := sqlmock.New()
if err != nil {
t.Fatal(err)
}
defer db.Close()
mock.ExpectBegin()
tx, err := db.Begin()
if err != nil {
t.Fatal(err)
}
mock.ExpectQuery("SELECT organization_id, is_default FROM user_organizations").WithArgs(int64(7)).WillReturnRows(sqlmock.NewRows([]string{"organization_id", "is_default"}).AddRow(1, false).AddRow(2, true))
mock.ExpectQuery("SELECT EXISTS").WithArgs(int64(7)).WillReturnRows(sqlmock.NewRows([]string{"admin"}).AddRow(false))
mock.ExpectQuery("SELECT id, status FROM organizations").WithArgs(int64(1), int64(2)).WillReturnRows(sqlmock.NewRows([]string{"id", "status"}).AddRow(1, "active").AddRow(2, "active"))
mock.ExpectExec("DELETE FROM user_organizations").WithArgs(int64(7)).WillReturnResult(sqlmock.NewResult(0, 2))
mock.ExpectExec("INSERT INTO user_organizations").WithArgs(int64(7), int64(1), true).WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectExec("INSERT INTO user_organizations").WithArgs(int64(7), int64(2), false).WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectExec("INSERT INTO organization_audit_logs").WithArgs(int64(99), nil, "organization_access_changed", `{"user_id":7,"organization_ids":[1,2],"default_organization_id":2}`, `{"user_id":7,"organization_ids":[1,2],"default_organization_id":1}`).WillReturnResult(sqlmock.NewResult(1, 1))
if err := replaceUserOrganizationsTx(tx, 7, model.UserOrganizationInput{DefaultOrganizationID: json.RawMessage("1"), ActorUserID: 99}, false); err != nil {
t.Fatal(err)
}
mock.ExpectRollback()
_ = tx.Rollback()
if err := mock.ExpectationsWereMet(); err != nil {
t.Fatal(err)
}
}
func TestUserOrganizationAuditFailureRollsBackUserAndRoles(t *testing.T) {
db, mock, _ := sqlmock.New()
defer db.Close()
database.DB = db
defer func() { database.DB = nil }()
mock.ExpectBegin()
mock.ExpectQuery("SELECT COUNT.*FROM roles").WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(1))
mock.ExpectQuery("SELECT id FROM roles WHERE code").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(1))
mock.ExpectQuery("SELECT COUNT.*FROM user_roles WHERE user_id").WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(0))
mock.ExpectQuery("SELECT id FROM users WHERE id.*FOR UPDATE").WithArgs(int64(7)).WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(7))
mock.ExpectExec("UPDATE users").WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectExec("DELETE FROM user_roles").WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectExec("INSERT INTO user_roles").WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectQuery("SELECT organization_id, is_default FROM user_organizations").WillReturnRows(sqlmock.NewRows([]string{"organization_id", "is_default"}).AddRow(2, true))
mock.ExpectQuery("SELECT EXISTS").WillReturnRows(sqlmock.NewRows([]string{"admin"}).AddRow(false))
mock.ExpectExec("DELETE FROM user_organizations").WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectExec("INSERT INTO organization_audit_logs").WillReturnError(errors.New("audit unavailable"))
mock.ExpectRollback()
ids := []int64{}
err := NewUserRepository().Update(&model.User{ID: 7}, []int64{2}, model.UserOrganizationInput{OrganizationIDs: &ids, DefaultOrganizationID: json.RawMessage("null"), ActorUserID: 99})
if err == nil || err.Error() != "audit unavailable" {
t.Fatalf("got %v", err)
}
if err := mock.ExpectationsWereMet(); err != nil {
t.Fatal(err)
}
}