136 lines
6.4 KiB
Go
136 lines
6.4 KiB
Go
package platform
|
|
|
|
import (
|
|
"database/sql/driver"
|
|
"net/http"
|
|
"regexp"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.apinb.com/bsm-sdk/core/errcode"
|
|
"git.apinb.com/bsm-sdk/core/types"
|
|
"github.com/DATA-DOG/go-sqlmock"
|
|
"google.golang.org/grpc/status"
|
|
)
|
|
|
|
func TestApprovalValuesOnlyChangesApprovalFieldsAndRecordsOperator(t *testing.T) {
|
|
values := approvalValues("approved", "accepted", "operator-a")
|
|
|
|
if values["status"] != "approved" || values["opinion"] != "accepted" || values["handler_identity"] != "operator-a" {
|
|
t.Fatalf("approval values do not retain the approved state, opinion, and operator: %#v", values)
|
|
}
|
|
if _, ok := values["handled_at"]; !ok {
|
|
t.Fatalf("approval values do not record handling time: %#v", values)
|
|
}
|
|
if len(values) != 4 {
|
|
t.Fatalf("approval update includes fields outside its whitelist: %#v", values)
|
|
}
|
|
}
|
|
|
|
func TestApproveAuditOnlyUpdatesApprovalFieldsAndAppendsOperationAudit(t *testing.T) {
|
|
_, mock := setupPlatformRoleDatabase(t)
|
|
now := time.Now().UTC()
|
|
mock.ExpectBegin()
|
|
mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "audit_approval" WHERE identity = $1 ORDER BY "audit_approval"."id" LIMIT $2`)).
|
|
WithArgs("approval-a", 1).
|
|
WillReturnRows(sqlmock.NewRows([]string{"id", "identity", "created_at", "updated_at", "status", "version", "business_type", "business_identity", "applicant_identity", "opinion", "handler_identity", "handled_at"}).
|
|
AddRow(uint64(1), "approval-a", now, now, "pending", 1, "refund", "payment-a", "applicant-a", "", "", nil))
|
|
mock.ExpectExec(regexp.QuoteMeta(`UPDATE "audit_approval" SET "handled_at"=$1,"handler_identity"=$2,"opinion"=$3,"status"=$4,"updated_at"=$5 WHERE identity = $6 AND status = $7`)).
|
|
WithArgs(sqlmock.AnyArg(), "operator-a", "accepted", "approved", sqlmock.AnyArg(), "approval-a", "pending").
|
|
WillReturnResult(sqlmock.NewResult(0, 1))
|
|
mock.ExpectQuery(regexp.QuoteMeta(`INSERT INTO "audit_operation_log" ("identity","created_at","updated_at","status","version","operator_identity","action","object_type","object_identity","before_data","after_data") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11) RETURNING "id"`)).
|
|
WithArgs(sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), "enabled", 1, "operator-a", "approve", "audit_approval", "approval-a", jsonContaining(`"status":"pending"`), jsonContaining(`"handler_identity":"operator-a"`)).
|
|
WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(uint64(2)))
|
|
mock.ExpectCommit()
|
|
|
|
ctx, recorder := updateContext(http.MethodPost, "/audit/audit_approval/approval-a/approve", "approval-a", []byte(`{"status":"approved","opinion":"accepted","business_identity":"payment-b"}`))
|
|
ctx.Set("Auth", &types.JwtClaims{Identity: "operator-a"})
|
|
ApproveAudit(ctx)
|
|
|
|
assertMockExpectations(t, mock)
|
|
assertResponseCode(t, recorder, 0)
|
|
if !strings.Contains(recorder.Body.String(), `"handler_identity":"operator-a"`) {
|
|
t.Fatalf("approval response omitted its handler: %s", recorder.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestApproveAuditRejectsStatusesOutsideApprovedAndRejected(t *testing.T) {
|
|
for _, decision := range []string{"pending", "archived"} {
|
|
t.Run(decision, func(t *testing.T) {
|
|
_, mock := setupPlatformRoleDatabase(t)
|
|
ctx, recorder := updateContext(http.MethodPost, "/audit/audit_approval/approval-a/approve", "approval-a", []byte(`{"status":"`+decision+`"}`))
|
|
ctx.Set("Auth", &types.JwtClaims{Identity: "operator-a"})
|
|
|
|
ApproveAudit(ctx)
|
|
|
|
assertResponseCode(t, recorder, int32(status.Code(errcode.ErrInvalidArgument)))
|
|
assertMockExpectations(t, mock)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestApproveAuditRejectsAlreadyHandledApproval(t *testing.T) {
|
|
_, mock := setupPlatformRoleDatabase(t)
|
|
mock.ExpectBegin()
|
|
mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "audit_approval" WHERE identity = $1 ORDER BY "audit_approval"."id" LIMIT $2`)).
|
|
WithArgs("approval-a", 1).
|
|
WillReturnRows(approvalRows("approval-a", "approved", "applicant-a"))
|
|
mock.ExpectRollback()
|
|
|
|
ctx, recorder := updateContext(http.MethodPost, "/audit/audit_approval/approval-a/approve", "approval-a", []byte(`{"status":"approved"}`))
|
|
ctx.Set("Auth", &types.JwtClaims{Identity: "operator-a"})
|
|
ApproveAudit(ctx)
|
|
|
|
assertResponseCode(t, recorder, int32(status.Code(errcode.ErrInvalidArgument)))
|
|
assertMockExpectations(t, mock)
|
|
}
|
|
|
|
func TestApproveAuditRejectsTheApplicant(t *testing.T) {
|
|
_, mock := setupPlatformRoleDatabase(t)
|
|
mock.ExpectBegin()
|
|
mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "audit_approval" WHERE identity = $1 ORDER BY "audit_approval"."id" LIMIT $2`)).
|
|
WithArgs("approval-a", 1).
|
|
WillReturnRows(approvalRows("approval-a", "pending", "operator-a"))
|
|
mock.ExpectRollback()
|
|
|
|
ctx, recorder := updateContext(http.MethodPost, "/audit/audit_approval/approval-a/approve", "approval-a", []byte(`{"status":"approved"}`))
|
|
ctx.Set("Auth", &types.JwtClaims{Identity: "operator-a"})
|
|
ApproveAudit(ctx)
|
|
|
|
assertResponseCode(t, recorder, int32(status.Code(errcode.ErrInvalidArgument)))
|
|
assertMockExpectations(t, mock)
|
|
}
|
|
|
|
func TestApproveAuditRejectsAConcurrentSecondDecision(t *testing.T) {
|
|
_, mock := setupPlatformRoleDatabase(t)
|
|
mock.ExpectBegin()
|
|
mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "audit_approval" WHERE identity = $1 ORDER BY "audit_approval"."id" LIMIT $2`)).
|
|
WithArgs("approval-a", 1).
|
|
WillReturnRows(approvalRows("approval-a", "pending", "applicant-a"))
|
|
mock.ExpectExec(regexp.QuoteMeta(`UPDATE "audit_approval" SET "handled_at"=$1,"handler_identity"=$2,"opinion"=$3,"status"=$4,"updated_at"=$5 WHERE identity = $6 AND status = $7`)).
|
|
WithArgs(sqlmock.AnyArg(), "operator-a", "", "approved", sqlmock.AnyArg(), "approval-a", "pending").
|
|
WillReturnResult(sqlmock.NewResult(0, 0))
|
|
mock.ExpectRollback()
|
|
|
|
ctx, recorder := updateContext(http.MethodPost, "/audit/audit_approval/approval-a/approve", "approval-a", []byte(`{"status":"approved"}`))
|
|
ctx.Set("Auth", &types.JwtClaims{Identity: "operator-a"})
|
|
ApproveAudit(ctx)
|
|
|
|
assertResponseCode(t, recorder, int32(status.Code(errcode.ErrInvalidArgument)))
|
|
assertMockExpectations(t, mock)
|
|
}
|
|
|
|
func approvalRows(identity, approvalStatus, applicant string) *sqlmock.Rows {
|
|
now := time.Now().UTC()
|
|
return sqlmock.NewRows([]string{"id", "identity", "created_at", "updated_at", "status", "version", "business_type", "business_identity", "applicant_identity", "opinion", "handler_identity", "handled_at"}).
|
|
AddRow(uint64(1), identity, now, now, approvalStatus, 1, "refund", "payment-a", applicant, "", "", nil)
|
|
}
|
|
|
|
type jsonContaining string
|
|
|
|
func (expected jsonContaining) Match(value driver.Value) bool {
|
|
actual, ok := value.(string)
|
|
return ok && strings.Contains(actual, string(expected))
|
|
}
|