mirror of
https://github.com/SecurityBrewery/catalyst.git
synced 2025-12-21 22:43:08 +01:00
feat: add reactions (#1074)
This commit is contained in:
250
testing/collection_reaction_test.go
Normal file
250
testing/collection_reaction_test.go
Normal file
@@ -0,0 +1,250 @@
|
||||
package testing
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestReactionsCollection(t *testing.T) {
|
||||
baseApp, adminToken, analystToken, baseAppCleanup := BaseApp(t)
|
||||
defer baseAppCleanup()
|
||||
|
||||
testSets := []authMatrixText{
|
||||
{
|
||||
baseTest: BaseTest{
|
||||
Name: "ListReactions",
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/collections/reactions/records",
|
||||
TestAppFactory: AppFactory(baseApp),
|
||||
},
|
||||
authBasedExpectations: []AuthBasedExpectation{
|
||||
{
|
||||
Name: "Unauthorized",
|
||||
ExpectedStatus: http.StatusOK,
|
||||
ExpectedContent: []string{
|
||||
`"totalItems":0`,
|
||||
`"items":[]`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{"OnRecordsListRequest": 1},
|
||||
},
|
||||
{
|
||||
Name: "Analyst",
|
||||
RequestHeaders: map[string]string{"Authorization": analystToken},
|
||||
ExpectedStatus: http.StatusOK,
|
||||
ExpectedContent: []string{
|
||||
`"totalItems":3`,
|
||||
`"id":"r_reaction"`,
|
||||
},
|
||||
NotExpectedContent: []string{
|
||||
`"items":[]`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{"OnRecordsListRequest": 1},
|
||||
},
|
||||
{
|
||||
Name: "Admin",
|
||||
RequestHeaders: map[string]string{"Authorization": adminToken},
|
||||
ExpectedStatus: http.StatusOK,
|
||||
ExpectedContent: []string{
|
||||
`"totalItems":3`,
|
||||
`"id":"r_reaction"`,
|
||||
},
|
||||
NotExpectedContent: []string{
|
||||
`"items":[]`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{"OnRecordsListRequest": 1},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
baseTest: BaseTest{
|
||||
Name: "CreateReaction",
|
||||
Method: http.MethodPost,
|
||||
RequestHeaders: map[string]string{"Content-Type": "application/json"},
|
||||
URL: "/api/collections/reactions/records",
|
||||
Body: s(map[string]any{
|
||||
"name": "test",
|
||||
"trigger": "webhook",
|
||||
"triggerdata": map[string]any{"path": "test"},
|
||||
"action": "python",
|
||||
"actiondata": map[string]any{"script": "print('Hello, World!')"},
|
||||
}),
|
||||
TestAppFactory: AppFactory(baseApp),
|
||||
},
|
||||
authBasedExpectations: []AuthBasedExpectation{
|
||||
{
|
||||
Name: "Unauthorized",
|
||||
ExpectedStatus: http.StatusBadRequest,
|
||||
ExpectedContent: []string{
|
||||
`"message":"Failed to create record."`,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Analyst",
|
||||
RequestHeaders: map[string]string{"Authorization": analystToken},
|
||||
ExpectedStatus: http.StatusOK,
|
||||
ExpectedContent: []string{
|
||||
`"name":"test"`,
|
||||
},
|
||||
NotExpectedContent: []string{
|
||||
`"items":[]`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{
|
||||
"OnModelAfterCreate": 1,
|
||||
"OnModelBeforeCreate": 1,
|
||||
"OnRecordAfterCreateRequest": 1,
|
||||
"OnRecordBeforeCreateRequest": 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Admin",
|
||||
RequestHeaders: map[string]string{"Authorization": adminToken},
|
||||
ExpectedStatus: http.StatusOK,
|
||||
ExpectedContent: []string{
|
||||
`"name":"test"`,
|
||||
},
|
||||
NotExpectedContent: []string{
|
||||
`"items":[]`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{
|
||||
"OnModelAfterCreate": 1,
|
||||
"OnModelBeforeCreate": 1,
|
||||
"OnRecordAfterCreateRequest": 1,
|
||||
"OnRecordBeforeCreateRequest": 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
baseTest: BaseTest{
|
||||
Name: "GetReaction",
|
||||
Method: http.MethodGet,
|
||||
RequestHeaders: map[string]string{"Content-Type": "application/json"},
|
||||
URL: "/api/collections/reactions/records/r_reaction",
|
||||
TestAppFactory: AppFactory(baseApp),
|
||||
},
|
||||
authBasedExpectations: []AuthBasedExpectation{
|
||||
{
|
||||
Name: "Unauthorized",
|
||||
ExpectedStatus: http.StatusNotFound,
|
||||
ExpectedContent: []string{
|
||||
`"message":"The requested resource wasn't found."`,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Analyst",
|
||||
RequestHeaders: map[string]string{"Authorization": analystToken},
|
||||
ExpectedStatus: http.StatusOK,
|
||||
ExpectedContent: []string{
|
||||
`"id":"r_reaction"`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{"OnRecordViewRequest": 1},
|
||||
},
|
||||
{
|
||||
Name: "Admin",
|
||||
RequestHeaders: map[string]string{"Authorization": adminToken},
|
||||
ExpectedStatus: http.StatusOK,
|
||||
ExpectedContent: []string{
|
||||
`"id":"r_reaction"`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{"OnRecordViewRequest": 1},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
baseTest: BaseTest{
|
||||
Name: "UpdateReaction",
|
||||
Method: http.MethodPatch,
|
||||
RequestHeaders: map[string]string{"Content-Type": "application/json"},
|
||||
URL: "/api/collections/reactions/records/r_reaction",
|
||||
Body: s(map[string]any{"name": "update"}),
|
||||
TestAppFactory: AppFactory(baseApp),
|
||||
},
|
||||
authBasedExpectations: []AuthBasedExpectation{
|
||||
{
|
||||
Name: "Unauthorized",
|
||||
ExpectedStatus: http.StatusNotFound,
|
||||
ExpectedContent: []string{
|
||||
`"message":"The requested resource wasn't found."`,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Analyst",
|
||||
RequestHeaders: map[string]string{"Authorization": analystToken},
|
||||
ExpectedStatus: http.StatusOK,
|
||||
ExpectedContent: []string{
|
||||
`"id":"r_reaction"`,
|
||||
`"name":"update"`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{
|
||||
"OnModelAfterUpdate": 1,
|
||||
"OnModelBeforeUpdate": 1,
|
||||
"OnRecordAfterUpdateRequest": 1,
|
||||
"OnRecordBeforeUpdateRequest": 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Admin",
|
||||
RequestHeaders: map[string]string{"Authorization": adminToken},
|
||||
ExpectedStatus: http.StatusOK,
|
||||
ExpectedContent: []string{
|
||||
`"id":"r_reaction"`,
|
||||
`"name":"update"`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{
|
||||
"OnModelAfterUpdate": 1,
|
||||
"OnModelBeforeUpdate": 1,
|
||||
"OnRecordAfterUpdateRequest": 1,
|
||||
"OnRecordBeforeUpdateRequest": 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
baseTest: BaseTest{
|
||||
Name: "DeleteReaction",
|
||||
Method: http.MethodDelete,
|
||||
URL: "/api/collections/reactions/records/r_reaction",
|
||||
TestAppFactory: AppFactory(baseApp),
|
||||
},
|
||||
authBasedExpectations: []AuthBasedExpectation{
|
||||
{
|
||||
Name: "Unauthorized",
|
||||
ExpectedStatus: http.StatusNotFound,
|
||||
ExpectedContent: []string{
|
||||
`"message":"The requested resource wasn't found."`,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Analyst",
|
||||
RequestHeaders: map[string]string{"Authorization": analystToken},
|
||||
ExpectedStatus: http.StatusNoContent,
|
||||
ExpectedEvents: map[string]int{
|
||||
"OnModelAfterDelete": 1,
|
||||
"OnModelBeforeDelete": 1,
|
||||
"OnRecordAfterDeleteRequest": 1,
|
||||
"OnRecordBeforeDeleteRequest": 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Admin",
|
||||
RequestHeaders: map[string]string{"Authorization": adminToken},
|
||||
ExpectedStatus: http.StatusNoContent,
|
||||
ExpectedEvents: map[string]int{
|
||||
"OnModelAfterDelete": 1,
|
||||
"OnModelBeforeDelete": 1,
|
||||
"OnRecordAfterDeleteRequest": 1,
|
||||
"OnRecordBeforeDeleteRequest": 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, testSet := range testSets {
|
||||
t.Run(testSet.baseTest.Name, func(t *testing.T) {
|
||||
for _, authBasedExpectation := range testSet.authBasedExpectations {
|
||||
scenario := mergeScenario(testSet.baseTest, authBasedExpectation)
|
||||
scenario.Test(t)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
177
testing/reaction_test.go
Normal file
177
testing/reaction_test.go
Normal file
@@ -0,0 +1,177 @@
|
||||
package testing
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestWebhookReactions(t *testing.T) {
|
||||
baseApp, adminToken, analystToken, baseAppCleanup := BaseApp(t)
|
||||
defer baseAppCleanup()
|
||||
|
||||
server := testWebhookServer()
|
||||
defer server.Close()
|
||||
|
||||
go server.ListenAndServe() //nolint:errcheck
|
||||
|
||||
testSets := []authMatrixText{
|
||||
{
|
||||
baseTest: BaseTest{
|
||||
Name: "TriggerWebhookReaction",
|
||||
Method: http.MethodGet,
|
||||
URL: "/reaction/test",
|
||||
TestAppFactory: AppFactory(baseApp),
|
||||
},
|
||||
authBasedExpectations: []AuthBasedExpectation{
|
||||
{
|
||||
Name: "Unauthorized",
|
||||
ExpectedStatus: http.StatusOK,
|
||||
ExpectedContent: []string{`Hello, World!`},
|
||||
},
|
||||
{
|
||||
Name: "Analyst",
|
||||
RequestHeaders: map[string]string{"Authorization": analystToken},
|
||||
ExpectedStatus: http.StatusOK,
|
||||
ExpectedContent: []string{`Hello, World!`},
|
||||
},
|
||||
{
|
||||
Name: "Admin",
|
||||
RequestHeaders: map[string]string{"Authorization": adminToken},
|
||||
ExpectedStatus: http.StatusOK,
|
||||
ExpectedContent: []string{`Hello, World!`},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
baseTest: BaseTest{
|
||||
Name: "TriggerWebhookReaction2",
|
||||
Method: http.MethodGet,
|
||||
URL: "/reaction/test2",
|
||||
TestAppFactory: AppFactory(baseApp),
|
||||
},
|
||||
authBasedExpectations: []AuthBasedExpectation{
|
||||
{
|
||||
Name: "Unauthorized",
|
||||
ExpectedStatus: http.StatusOK,
|
||||
ExpectedContent: []string{`"test":true`},
|
||||
},
|
||||
{
|
||||
Name: "Analyst",
|
||||
RequestHeaders: map[string]string{"Authorization": analystToken},
|
||||
ExpectedStatus: http.StatusOK,
|
||||
ExpectedContent: []string{`"test":true`},
|
||||
},
|
||||
{
|
||||
Name: "Admin",
|
||||
RequestHeaders: map[string]string{"Authorization": adminToken},
|
||||
ExpectedStatus: http.StatusOK,
|
||||
ExpectedContent: []string{`"test":true`},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, testSet := range testSets {
|
||||
t.Run(testSet.baseTest.Name, func(t *testing.T) {
|
||||
for _, authBasedExpectation := range testSet.authBasedExpectations {
|
||||
scenario := mergeScenario(testSet.baseTest, authBasedExpectation)
|
||||
scenario.Test(t)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func testWebhookServer() *http.Server {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/webhook", func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write([]byte(`{"test":true}`)) //nolint:errcheck
|
||||
})
|
||||
|
||||
return &http.Server{
|
||||
Addr: "127.0.0.1:12345",
|
||||
Handler: mux,
|
||||
ReadHeaderTimeout: 3 * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
type RecordingServer struct {
|
||||
Entries []string
|
||||
}
|
||||
|
||||
func NewRecordingServer() *RecordingServer {
|
||||
return &RecordingServer{}
|
||||
}
|
||||
|
||||
func (s *RecordingServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
s.Entries = append(s.Entries, r.URL.Path)
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write([]byte(`{"test":true}`)) //nolint:errcheck
|
||||
}
|
||||
|
||||
func TestHookReactions(t *testing.T) {
|
||||
baseApp, _, analystToken, baseAppCleanup := BaseApp(t)
|
||||
defer baseAppCleanup()
|
||||
|
||||
server := NewRecordingServer()
|
||||
|
||||
go http.ListenAndServe("127.0.0.1:12346", server) //nolint:gosec,errcheck
|
||||
|
||||
testSets := []authMatrixText{
|
||||
{
|
||||
baseTest: BaseTest{
|
||||
Name: "TriggerHookReaction",
|
||||
Method: http.MethodPost,
|
||||
RequestHeaders: map[string]string{"Content-Type": "application/json"},
|
||||
URL: "/api/collections/tickets/records",
|
||||
Body: s(map[string]any{
|
||||
"name": "test",
|
||||
}),
|
||||
TestAppFactory: AppFactory(baseApp),
|
||||
},
|
||||
authBasedExpectations: []AuthBasedExpectation{
|
||||
// {
|
||||
// Name: "Unauthorized",
|
||||
// ExpectedStatus: http.StatusOK,
|
||||
// ExpectedContent: []string{`Hello, World!`},
|
||||
// },
|
||||
{
|
||||
Name: "Analyst",
|
||||
RequestHeaders: map[string]string{"Authorization": analystToken},
|
||||
ExpectedStatus: http.StatusOK,
|
||||
ExpectedContent: []string{
|
||||
`"collectionName":"tickets"`,
|
||||
`"name":"test"`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{
|
||||
"OnModelAfterCreate": 1,
|
||||
"OnModelBeforeCreate": 1,
|
||||
"OnRecordAfterCreateRequest": 1,
|
||||
"OnRecordBeforeCreateRequest": 1,
|
||||
},
|
||||
},
|
||||
// {
|
||||
// Name: "Admin",
|
||||
// RequestHeaders: map[string]string{"Authorization": adminToken},
|
||||
// ExpectedStatus: http.StatusOK,
|
||||
// ExpectedContent: []string{`Hello, World!`},
|
||||
// },
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, testSet := range testSets {
|
||||
t.Run(testSet.baseTest.Name, func(t *testing.T) {
|
||||
for _, authBasedExpectation := range testSet.authBasedExpectations {
|
||||
scenario := mergeScenario(testSet.baseTest, authBasedExpectation)
|
||||
scenario.Test(t)
|
||||
}
|
||||
|
||||
require.NotEmpty(t, server.Entries)
|
||||
})
|
||||
}
|
||||
}
|
||||
79
testing/routes_test.go
Normal file
79
testing/routes_test.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package testing
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_Routes(t *testing.T) {
|
||||
baseApp, adminToken, analystToken, baseAppCleanup := BaseApp(t)
|
||||
defer baseAppCleanup()
|
||||
|
||||
testSets := []authMatrixText{
|
||||
{
|
||||
baseTest: BaseTest{
|
||||
Name: "Root",
|
||||
Method: http.MethodGet,
|
||||
URL: "/",
|
||||
TestAppFactory: AppFactory(baseApp),
|
||||
},
|
||||
authBasedExpectations: []AuthBasedExpectation{
|
||||
{
|
||||
Name: "Unauthorized",
|
||||
ExpectedStatus: http.StatusFound,
|
||||
},
|
||||
{
|
||||
Name: "Analyst",
|
||||
RequestHeaders: map[string]string{"Authorization": analystToken},
|
||||
ExpectedStatus: http.StatusFound,
|
||||
},
|
||||
{
|
||||
Name: "Admin",
|
||||
RequestHeaders: map[string]string{"Authorization": adminToken},
|
||||
ExpectedStatus: http.StatusFound,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
baseTest: BaseTest{
|
||||
Name: "Config",
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/config",
|
||||
TestAppFactory: AppFactory(baseApp),
|
||||
},
|
||||
authBasedExpectations: []AuthBasedExpectation{
|
||||
{
|
||||
Name: "Unauthorized",
|
||||
ExpectedStatus: http.StatusOK,
|
||||
ExpectedContent: []string{
|
||||
`"flags":null`,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Analyst",
|
||||
RequestHeaders: map[string]string{"Authorization": analystToken},
|
||||
ExpectedStatus: http.StatusOK,
|
||||
ExpectedContent: []string{
|
||||
`"flags":null`,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Admin",
|
||||
RequestHeaders: map[string]string{"Authorization": adminToken},
|
||||
ExpectedStatus: http.StatusOK,
|
||||
ExpectedContent: []string{
|
||||
`"flags":null`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, testSet := range testSets {
|
||||
t.Run(testSet.baseTest.Name, func(t *testing.T) {
|
||||
for _, authBasedExpectation := range testSet.authBasedExpectations {
|
||||
scenario := mergeScenario(testSet.baseTest, authBasedExpectation)
|
||||
scenario.Test(t)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
103
testing/testdata.go
Normal file
103
testing/testdata.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package testing
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"github.com/pocketbase/pocketbase/models"
|
||||
|
||||
"github.com/SecurityBrewery/catalyst/migrations"
|
||||
)
|
||||
|
||||
const (
|
||||
adminEmail = "admin@catalyst-soar.com"
|
||||
analystEmail = "analyst@catalyst-soar.com"
|
||||
)
|
||||
|
||||
func defaultTestData(t *testing.T, app core.App) {
|
||||
t.Helper()
|
||||
|
||||
adminTestData(t, app)
|
||||
userTestData(t, app)
|
||||
reactionTestData(t, app)
|
||||
}
|
||||
|
||||
func adminTestData(t *testing.T, app core.App) {
|
||||
t.Helper()
|
||||
|
||||
admin := &models.Admin{Email: adminEmail}
|
||||
|
||||
if err := admin.SetPassword("password"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := app.Dao().SaveAdmin(admin); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func userTestData(t *testing.T, app core.App) {
|
||||
t.Helper()
|
||||
|
||||
collection, err := app.Dao().FindCollectionByNameOrId(migrations.UserCollectionName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
record := models.NewRecord(collection)
|
||||
record.SetId("u_bob_analyst")
|
||||
_ = record.SetUsername("u_bob_analyst")
|
||||
_ = record.SetPassword("password")
|
||||
record.Set("name", "Bob Analyst")
|
||||
record.Set("email", analystEmail)
|
||||
_ = record.SetVerified(true)
|
||||
|
||||
if err := app.Dao().SaveRecord(record); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func reactionTestData(t *testing.T, app core.App) {
|
||||
t.Helper()
|
||||
|
||||
collection, err := app.Dao().FindCollectionByNameOrId(migrations.ReactionCollectionName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
record := models.NewRecord(collection)
|
||||
record.SetId("r_reaction")
|
||||
record.Set("name", "Reaction")
|
||||
record.Set("trigger", "webhook")
|
||||
record.Set("triggerdata", `{"path":"test"}`)
|
||||
record.Set("action", "python")
|
||||
record.Set("actiondata", `{"bootstrap":"requests","script":"print('Hello, World!')"}`)
|
||||
|
||||
if err := app.Dao().SaveRecord(record); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
record = models.NewRecord(collection)
|
||||
record.SetId("r_reaction_webhook")
|
||||
record.Set("name", "Reaction")
|
||||
record.Set("trigger", "webhook")
|
||||
record.Set("triggerdata", `{"path":"test2"}`)
|
||||
record.Set("action", "webhook")
|
||||
record.Set("actiondata", `{"headers":{"Content-Type":"application/json"},"url":"http://127.0.0.1:12345/webhook"}`)
|
||||
|
||||
if err := app.Dao().SaveRecord(record); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
record = models.NewRecord(collection)
|
||||
record.SetId("r_reaction_hook")
|
||||
record.Set("name", "Hook")
|
||||
record.Set("trigger", "hook")
|
||||
record.Set("triggerdata", `{"collections":["tickets"],"events":["create"]}`)
|
||||
record.Set("action", "python")
|
||||
record.Set("actiondata", `{"bootstrap":"requests","script":"import requests\nrequests.post('http://127.0.0.1:12346/test', json={'test':True})"}`)
|
||||
|
||||
if err := app.Dao().SaveRecord(record); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
172
testing/testing.go
Normal file
172
testing/testing.go
Normal file
@@ -0,0 +1,172 @@
|
||||
package testing
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"github.com/pocketbase/pocketbase/tests"
|
||||
"github.com/pocketbase/pocketbase/tokens"
|
||||
|
||||
"github.com/SecurityBrewery/catalyst/app"
|
||||
"github.com/SecurityBrewery/catalyst/migrations"
|
||||
)
|
||||
|
||||
func BaseApp(t *testing.T) (core.App, string, string, func()) {
|
||||
t.Helper()
|
||||
|
||||
temp, err := os.MkdirTemp("", "catalyst_test_data")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
baseApp := app.App(temp)
|
||||
|
||||
if err := app.Bootstrap(baseApp); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
defaultTestData(t, baseApp)
|
||||
|
||||
adminToken, err := generateAdminToken(t, baseApp, adminEmail)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
analystToken, err := generateRecordToken(t, baseApp, analystEmail)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return baseApp, adminToken, analystToken, func() { _ = os.RemoveAll(temp) }
|
||||
}
|
||||
|
||||
func AppFactory(baseApp core.App) func(t *testing.T) *tests.TestApp {
|
||||
return func(t *testing.T) *tests.TestApp {
|
||||
t.Helper()
|
||||
|
||||
testApp, err := tests.NewTestApp(baseApp.DataDir())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
app.BindHooks(testApp)
|
||||
|
||||
if err := app.Bootstrap(testApp); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return testApp
|
||||
}
|
||||
}
|
||||
|
||||
func App(t *testing.T) (*tests.TestApp, func()) {
|
||||
t.Helper()
|
||||
|
||||
baseApp, _, _, cleanup := BaseApp(t)
|
||||
|
||||
testApp := AppFactory(baseApp)(t)
|
||||
|
||||
return testApp, cleanup
|
||||
}
|
||||
|
||||
func generateAdminToken(t *testing.T, baseApp core.App, email string) (string, error) {
|
||||
t.Helper()
|
||||
|
||||
app, err := tests.NewTestApp(baseApp.DataDir())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer app.Cleanup()
|
||||
|
||||
admin, err := app.Dao().FindAdminByEmail(email)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return tokens.NewAdminAuthToken(app, admin)
|
||||
}
|
||||
|
||||
func generateRecordToken(t *testing.T, baseApp core.App, email string) (string, error) {
|
||||
t.Helper()
|
||||
|
||||
app, err := tests.NewTestApp(baseApp.DataDir())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer app.Cleanup()
|
||||
|
||||
record, err := app.Dao().FindAuthRecordByEmail(migrations.UserCollectionName, email)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return tokens.NewRecordAuthToken(app, record)
|
||||
}
|
||||
|
||||
type BaseTest struct {
|
||||
Name string
|
||||
Method string
|
||||
RequestHeaders map[string]string
|
||||
URL string
|
||||
Body string
|
||||
TestAppFactory func(t *testing.T) *tests.TestApp
|
||||
}
|
||||
|
||||
type AuthBasedExpectation struct {
|
||||
Name string
|
||||
RequestHeaders map[string]string
|
||||
ExpectedStatus int
|
||||
ExpectedContent []string
|
||||
NotExpectedContent []string
|
||||
ExpectedEvents map[string]int
|
||||
}
|
||||
|
||||
type authMatrixText struct {
|
||||
baseTest BaseTest
|
||||
authBasedExpectations []AuthBasedExpectation
|
||||
}
|
||||
|
||||
func mergeScenario(base BaseTest, expectation AuthBasedExpectation) tests.ApiScenario {
|
||||
return tests.ApiScenario{
|
||||
Name: expectation.Name,
|
||||
Method: base.Method,
|
||||
Url: base.URL,
|
||||
Body: bytes.NewBufferString(base.Body),
|
||||
TestAppFactory: base.TestAppFactory,
|
||||
|
||||
RequestHeaders: mergeMaps(base.RequestHeaders, expectation.RequestHeaders),
|
||||
ExpectedStatus: expectation.ExpectedStatus,
|
||||
ExpectedContent: expectation.ExpectedContent,
|
||||
NotExpectedContent: expectation.NotExpectedContent,
|
||||
ExpectedEvents: expectation.ExpectedEvents,
|
||||
}
|
||||
}
|
||||
|
||||
func mergeMaps(a, b map[string]string) map[string]string {
|
||||
if a == nil {
|
||||
return b
|
||||
}
|
||||
|
||||
if b == nil {
|
||||
return a
|
||||
}
|
||||
|
||||
for k, v := range b {
|
||||
a[k] = v
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
func b(data map[string]any) []byte {
|
||||
b, _ := json.Marshal(data) //nolint:errchkjson
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
func s(data map[string]any) string {
|
||||
return string(b(data))
|
||||
}
|
||||
Reference in New Issue
Block a user