package handler import ( "bytes" "context" "encoding/json" "fmt" "git.infra.nkode.tech/dkelly/nkode-core/api" "git.infra.nkode.tech/dkelly/nkode-core/email" "git.infra.nkode.tech/dkelly/nkode-core/entities" "git.infra.nkode.tech/dkelly/nkode-core/models" "git.infra.nkode.tech/dkelly/nkode-core/repository" "git.infra.nkode.tech/dkelly/nkode-core/security" "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" "log" "net/http" "net/http/httptest" "os" "testing" ) func TestNkodeAPI(t *testing.T) { tr := NewTestRouter() tr.Start() defer func(tr *TestRouter) { err := tr.Stop() assert.NoError(t, err) }(tr) // *** Create New Customer *** customerID, status, err := tr.CreateNewCustomerDefaultPolicy() assert.NoError(t, err) assert.Equal(t, 200, status) attrPerKey := 9 numKeys := 6 userEmail := "test_username" + security.GenerateRandomString(12) + "@example.com" reset := false // *** Signup *** resp, status, err := tr.Signup(customerID, attrPerKey, numKeys, userEmail, reset) assert.NoError(t, err) assert.Equal(t, 200, status) passcodeLen := 4 userPasscode := resp.UserIdxInterface[:passcodeLen] kpSet := entities.KeypadDimension{ AttrsPerKey: numKeys, NumbOfKeys: numKeys, } setKeySelection, err := entities.SelectKeyByAttrIdx(resp.UserIdxInterface, userPasscode, kpSet) assert.NoError(t, err) // *** Set nKode *** confirmInterface, status, err := tr.SetNKode(customerID, setKeySelection, resp.SessionId) assert.NoError(t, err) assert.Equal(t, 200, status) confirmKeySelection, err := entities.SelectKeyByAttrIdx(confirmInterface, userPasscode, kpSet) assert.NoError(t, err) // *** Confirm nKode *** status, err = tr.ConfirmNKode(customerID, confirmKeySelection, resp.SessionId) assert.NoError(t, err) assert.Equal(t, 200, status) // *** Get Login Interface *** loginInterface, status, err := tr.GetLoginInterface(userEmail, customerID) assert.NoError(t, err) assert.Equal(t, 200, status) kp := entities.KeypadDimension{ AttrsPerKey: attrPerKey, NumbOfKeys: numKeys, } loginKeySelection, err := entities.SelectKeyByAttrIdx(loginInterface.UserIdxInterface, userPasscode, kp) assert.NoError(t, err) // *** Login *** tokens, status, err := tr.Login(customerID, userEmail, loginKeySelection) assert.NoError(t, err) assert.Equal(t, 200, status) assert.NotEmpty(t, tokens.AccessToken) assert.NotEmpty(t, tokens.RefreshToken) // *** Renew Attributes *** } type TestRouter struct { Router *gin.Engine EmailQueue *email.Queue Repo *repository.SqliteRepository Handler *NkodeHandler } func NewTestRouter() *TestRouter { gin.SetMode(gin.TestMode) router := gin.Default() logger := log.Default() ctx := context.Background() dbPath := os.Getenv("TEST_DB") repo, err := repository.NewSqliteRepository(ctx, dbPath) if err != nil { log.Fatal(err) } sesClient := email.NewSESClient() emailQueue := email.NewEmailQueue(email.EmailQueueBufferSize, email.MaxEmailsPerSecond, &sesClient) nkodeAPI := api.NewNKodeAPI(repo, emailQueue) h := NkodeHandler{ API: nkodeAPI, Logger: logger, } h.RegisterRoutes(router) return &TestRouter{ Handler: &h, Router: router, EmailQueue: emailQueue, Repo: repo, } } func (r *TestRouter) Start() { r.Repo.Start() r.EmailQueue.Start() } func (r *TestRouter) Stop() error { r.EmailQueue.Stop() return r.Repo.Stop() } func (r *TestRouter) CreateNewCustomerDefaultPolicy() (string, int, error) { p := entities.NewDefaultNKodePolicy() body := bytes.NewBufferString(fmt.Sprintf( "max_nkode_len=%d&min_nkode_len=%d&distinct_sets=%d&distinct_attributes=%d&lock_out=%d&expiration=%d", p.MaxNkodeLen, p.MinNkodeLen, p.DistinctSets, p.DistinctAttributes, p.LockOut, p.Expiration, )) req := httptest.NewRequest(http.MethodPost, "/create-new-customer", body) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") rec := httptest.NewRecorder() r.Router.ServeHTTP(rec, req) var resp struct { CustomerID string `json:"customer_id"` } if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil { return "", rec.Code, err } return resp.CustomerID, rec.Code, nil } func (r *TestRouter) Signup( customerID string, attrsPerKey int, numberOfKeys int, userEmail string, reset bool, ) (*entities.SignupResetInterface, int, error) { body := bytes.NewBufferString(fmt.Sprintf( "customer_id=%s&attrs_per_key=%d&numb_of_keys=%d&email=%s&reset=%t", customerID, attrsPerKey, numberOfKeys, userEmail, reset, )) req := httptest.NewRequest(http.MethodPost, "/signup", body) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") rec := httptest.NewRecorder() r.Router.ServeHTTP(rec, req) var resp entities.SignupResetInterface if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil { return nil, rec.Code, err } return &resp, rec.Code, nil } func (r *TestRouter) SetNKode( customerID string, selection []int, sessionID string, ) ([]int, int, error) { data := models.SetNKodePost{ CustomerId: customerID, KeySelection: selection, SessionId: sessionID, } body, err := json.Marshal(data) if err != nil { return nil, 0, err } req := httptest.NewRequest(http.MethodPost, "/set-nkode", bytes.NewBuffer(body)) req.Header.Set("Content-Type", "application/json") rec := httptest.NewRecorder() r.Router.ServeHTTP(rec, req) var resp struct { UserInterface []int `json:"user_interface"` } if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil { return nil, rec.Code, err } return resp.UserInterface, rec.Code, nil } func (r *TestRouter) ConfirmNKode( customerID string, selection entities.KeySelection, sessionID string, ) (int, error) { data := models.ConfirmNKodePost{ CustomerId: customerID, KeySelection: selection, SessionId: sessionID, } body, err := json.Marshal(data) if err != nil { return 0, err } req := httptest.NewRequest(http.MethodPost, "/confirm-nkode", bytes.NewBuffer(body)) req.Header.Set("Content-Type", "application/json") rec := httptest.NewRecorder() r.Router.ServeHTTP(rec, req) return rec.Code, nil } func (r *TestRouter) GetLoginInterface( userEmail string, customerID string, ) (entities.LoginInterface, int, error) { body := bytes.NewBufferString(fmt.Sprintf( "email=%s&customer_id=%s", userEmail, customerID, )) req := httptest.NewRequest(http.MethodPost, "/get-login-interface", body) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") rec := httptest.NewRecorder() r.Router.ServeHTTP(rec, req) var resp entities.LoginInterface if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil { return entities.LoginInterface{}, rec.Code, err } return resp, rec.Code, nil } func (r *TestRouter) Login( customerID string, userEmail string, selection []int, ) (security.AuthenticationTokens, int, error) { data := models.LoginPost{ CustomerId: customerID, UserEmail: userEmail, KeySelection: selection, } body, err := json.Marshal(data) if err != nil { return security.AuthenticationTokens{}, 0, err } req := httptest.NewRequest(http.MethodPost, "/login", bytes.NewBuffer(body)) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") rec := httptest.NewRecorder() r.Router.ServeHTTP(rec, req) var resp security.AuthenticationTokens if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil { return security.AuthenticationTokens{}, rec.Code, err } return resp, rec.Code, nil } func (r *TestRouter) RenewAttributes( customerID string, ) (int, error) { data := models.RenewAttributesPost{ CustomerId: customerID, } body, err := json.Marshal(data) if err != nil { return 0, err } req := httptest.NewRequest(http.MethodPost, "/renew-attributes", bytes.NewBuffer(body)) req.Header.Set("Content-Type", "application/json") rec := httptest.NewRecorder() r.Router.ServeHTTP(rec, req) return rec.Code, nil }