Compare commits

...

6 Commits

7 changed files with 271 additions and 101 deletions

View File

@ -2,23 +2,86 @@ package driver
import ( import (
"encoding/json" "encoding/json"
"log"
"os" "os"
"regexp"
) )
type StepTest struct { type Step struct {
Input string `json:"input"` Input string `json:"input"`
ExpectedContent string `json:"expectedContent"` ExpectedContent string `json:"expectedContent"`
} }
// Group represents a group of steps. func (s *Step) MatchesExpectedContent(content []byte) (bool, error) {
type GroupTest struct { pattern := `.*\?.*|.*`
re, err := regexp.Compile(pattern)
if err != nil {
return false, err
}
// Check if the content matches the regex pattern
if re.Match(content) {
return true, nil
}
return false, nil
}
// Group represents a group of steps
type Group struct {
Name string `json:"name"` Name string `json:"name"`
Steps []Step `json:"steps"` Steps []Step `json:"steps"`
} }
type TestCase struct {
Name string
Input string
ExpectedContent string
}
func (s *TestCase) MatchesExpectedContent(content []byte) (bool, error) {
re, err := regexp.Compile(s.ExpectedContent)
if err != nil {
return false, err
}
// Check if the content matches the regex pattern
if re.Match(content) {
return true, nil
}
return false, nil
}
// DataGroup represents the overall structure of the JSON. // DataGroup represents the overall structure of the JSON.
type DataGroup struct { type DataGroup struct {
Groups []GroupTest `json:"groups"` Groups []Group `json:"groups"`
}
type Session struct {
Name string `json:"name"`
Groups []Group `json:"groups"`
}
func ReadData() []Session {
data, err := os.ReadFile("test_setup.json")
if err != nil {
log.Fatalf("Failed to read file: %v", err)
}
// Unmarshal JSON data
var sessions []Session
err = json.Unmarshal(data, &sessions)
if err != nil {
log.Fatalf("Failed to unmarshal JSON: %v", err)
}
return sessions
}
func FilterGroupsByName(groups []Group, name string) []Group {
var filteredGroups []Group
for _, group := range groups {
if group.Name == name {
filteredGroups = append(filteredGroups, group)
}
}
return filteredGroups
} }
func LoadTestGroups(filePath string) (DataGroup, error) { func LoadTestGroups(filePath string) (DataGroup, error) {
@ -31,24 +94,12 @@ func LoadTestGroups(filePath string) (DataGroup, error) {
return sessionsData, err return sessionsData, err
} }
func CreateTestCases(group DataGroup) []struct { func CreateTestCases(group DataGroup) []TestCase {
Name string var tests []TestCase
Input string
ExpectedContent string
} {
var tests []struct {
Name string
Input string
ExpectedContent string
}
for _, group := range group.Groups { for _, group := range group.Groups {
for _, step := range group.Steps { for _, step := range group.Steps {
// Create a test case for each group // Create a test case for each group
tests = append(tests, struct { tests = append(tests, TestCase{
Name string
Input string
ExpectedContent string
}{
Name: group.Name, Name: group.Name,
Input: step.Input, Input: step.Input,
ExpectedContent: step.ExpectedContent, ExpectedContent: step.ExpectedContent,

View File

@ -1,55 +0,0 @@
package driver
import (
"encoding/json"
"log"
"os"
)
type Step struct {
Input string `json:"input"`
ExpectedContent string `json:"expectedContent"`
}
type Group struct {
Name string `json:"name"`
Steps []Step `json:"steps"`
}
type Session struct {
Name string `json:"name"`
Groups []Group `json:"groups"`
}
func ReadData() []Session {
data, err := os.ReadFile("test_setup.json")
if err != nil {
log.Fatalf("Failed to read file: %v", err)
}
// Unmarshal JSON data
var sessions []Session
err = json.Unmarshal(data, &sessions)
if err != nil {
log.Fatalf("Failed to unmarshal JSON: %v", err)
}
return sessions
}
func FilterGroupsByName(groups []Group, name string) []Group {
var filteredGroups []Group
for _, group := range groups {
if group.Name == name {
filteredGroups = append(filteredGroups, group)
}
}
return filteredGroups
}
func Map[T any, U any](input []T, fn func(T) U) []U {
result := make([]U, len(input))
for i, v := range input {
result[i] = fn(v)
}
return result
}

View File

@ -0,0 +1,119 @@
package testutil
import (
"context"
"fmt"
"os"
"path"
"time"
"git.defalsify.org/vise.git/engine"
"git.defalsify.org/vise.git/logging"
"git.defalsify.org/vise.git/resource"
"git.grassecon.net/urdt/ussd/internal/handlers"
"git.grassecon.net/urdt/ussd/internal/handlers/server"
"git.grassecon.net/urdt/ussd/internal/storage"
testdataloader "github.com/peteole/testdata-loader"
)
var (
baseDir = testdataloader.GetBasePath()
logg = logging.NewVanilla()
scriptDir = path.Join(baseDir, "services", "registration")
)
func TestEngine(sessionId string) (engine.Engine, func(), chan bool) {
ctx := context.Background()
ctx = context.WithValue(ctx, "SessionId", sessionId)
pfp := path.Join(scriptDir, "pp.csv")
var eventChannel = make(chan bool)
cfg := engine.Config{
Root: "root",
SessionId: sessionId,
OutputSize: uint32(160),
FlagCount: uint32(16),
}
dbDir := ".test_state"
resourceDir := scriptDir
menuStorageService := storage.NewMenuStorageService(dbDir, resourceDir)
err := menuStorageService.EnsureDbDir()
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
rs, err := menuStorageService.GetResource(ctx)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
pe, err := menuStorageService.GetPersister(ctx)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
userDataStore, err := menuStorageService.GetUserdataDb(ctx)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
dbResource, ok := rs.(*resource.DbResource)
if !ok {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
lhs, err := handlers.NewLocalHandlerService(pfp, true, dbResource, cfg, rs)
lhs.SetDataStore(&userDataStore)
lhs.SetPersister(pe)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
switch AccountService.(type) {
case *server.MockAccountService:
go func() {
eventChannel <- false
}()
case *server.AccountService:
go func() {
time.Sleep(5 * time.Second) // Wait for 5 seconds
eventChannel <- true
}()
default:
panic("Unknown account service type")
}
hl, err := lhs.GetHandler(AccountService)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
en := lhs.GetEngine()
en = en.WithFirst(hl.Init)
cleanFn := func() {
err := en.Finish()
if err != nil {
logg.Errorf(err.Error())
}
err = menuStorageService.Close()
if err != nil {
logg.Errorf(err.Error())
}
logg.Infof("testengine storage closed")
}
//en = en.WithDebug(nil)
return en, cleanFn, eventChannel
}

View File

@ -0,0 +1,13 @@
// +build !online
package testutil
import (
"git.grassecon.net/urdt/ussd/internal/handlers/server"
)
var AccountService server.AccountServiceInterface
func init() {
AccountService = &server.MockAccountService{}
}

View File

@ -0,0 +1,11 @@
// +build online
package testutil
import "git.grassecon.net/urdt/ussd/internal/handlers/server"
var AccountService server.AccountServiceInterface
func init() {
AccountService = &server.AccountService{}
}

View File

@ -19,4 +19,3 @@ INCMP enter_yob 4
INCMP enter_location 5 INCMP enter_location 5
INCMP enter_offerings 6 INCMP enter_offerings 6
INCMP view_profile 7 INCMP view_profile 7

View File

@ -8,10 +8,9 @@ import (
"os" "os"
"regexp" "regexp"
"testing" "testing"
"time"
"git.grassecon.net/urdt/ussd/driver" "git.grassecon.net/urdt/ussd/driver"
enginetest "git.grassecon.net/urdt/ussd/engine" "git.grassecon.net/urdt/ussd/internal/testutil"
"github.com/gofrs/uuid" "github.com/gofrs/uuid"
) )
@ -55,7 +54,7 @@ func TestMain(m *testing.M) {
} }
func TestAccountCreationSuccessful(t *testing.T) { func TestAccountCreationSuccessful(t *testing.T) {
en, fn := enginetest.TestEngine(sessionID) en, fn, eventChannel := testutil.TestEngine(sessionID)
defer fn() defer fn()
ctx := context.Background() ctx := context.Background()
sessions := testData sessions := testData
@ -76,14 +75,18 @@ func TestAccountCreationSuccessful(t *testing.T) {
t.Fatalf("Test case '%s' failed during Flush: %v", group.Name, err) t.Fatalf("Test case '%s' failed during Flush: %v", group.Name, err)
} }
b := w.Bytes() b := w.Bytes()
if !bytes.Equal(b, []byte(step.ExpectedContent)) { match, err := step.MatchesExpectedContent(b)
if err != nil {
t.Fatalf("Error compiling regex for step '%s': %v", step.Input, err)
}
if !match {
t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", step.ExpectedContent, b) t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", step.ExpectedContent, b)
} }
} }
} }
} }
// Adding a sleep after the test to wait for registration to complete the process <-eventChannel
time.Sleep(5 * time.Second)
} }
func TestAccountRegistrationRejectTerms(t *testing.T) { func TestAccountRegistrationRejectTerms(t *testing.T) {
@ -94,7 +97,7 @@ func TestAccountRegistrationRejectTerms(t *testing.T) {
t.Fail() t.Fail()
} }
edgeCaseSessionID := v.String() edgeCaseSessionID := v.String()
en, fn := enginetest.TestEngine(edgeCaseSessionID) en, fn, _ := testutil.TestEngine(edgeCaseSessionID)
defer fn() defer fn()
ctx := context.Background() ctx := context.Background()
sessions := testData sessions := testData
@ -116,7 +119,11 @@ func TestAccountRegistrationRejectTerms(t *testing.T) {
} }
b := w.Bytes() b := w.Bytes()
if !bytes.Equal(b, []byte(step.ExpectedContent)) { match, err := step.MatchesExpectedContent(b)
if err != nil {
t.Fatalf("Error compiling regex for step '%s': %v", step.Input, err)
}
if !match {
t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", step.ExpectedContent, b) t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", step.ExpectedContent, b)
} }
} }
@ -125,7 +132,7 @@ func TestAccountRegistrationRejectTerms(t *testing.T) {
} }
func TestSendWithInvalidInputs(t *testing.T) { func TestSendWithInvalidInputs(t *testing.T) {
en, fn := enginetest.TestEngine(sessionID) en, fn, _ := testutil.TestEngine(sessionID)
defer fn() defer fn()
ctx := context.Background() ctx := context.Background()
sessions := testData sessions := testData
@ -153,8 +160,11 @@ func TestSendWithInvalidInputs(t *testing.T) {
// Replace placeholder {public_key} with the actual dynamic public key // Replace placeholder {public_key} with the actual dynamic public key
expectedContent := bytes.Replace([]byte(step.ExpectedContent), []byte("{public_key}"), []byte(publicKey), -1) expectedContent := bytes.Replace([]byte(step.ExpectedContent), []byte("{public_key}"), []byte(publicKey), -1)
match, err := step.MatchesExpectedContent(b)
if !bytes.Equal(b, expectedContent) { if err != nil {
t.Fatalf("Error compiling regex for step '%s': %v", step.Input, err)
}
if !match {
t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", expectedContent, b) t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", expectedContent, b)
} }
} }
@ -163,7 +173,7 @@ func TestSendWithInvalidInputs(t *testing.T) {
} }
func TestMyAccount_Check_My_Balance(t *testing.T) { func TestMyAccount_Check_My_Balance(t *testing.T) {
en, fn := enginetest.TestEngine(sessionID) en, fn, _ := testutil.TestEngine(sessionID)
defer fn() defer fn()
ctx := context.Background() ctx := context.Background()
sessions := testData sessions := testData
@ -185,7 +195,11 @@ func TestMyAccount_Check_My_Balance(t *testing.T) {
t.Errorf("Test case '%s' failed during Flush: %v", group.Name, err) t.Errorf("Test case '%s' failed during Flush: %v", group.Name, err)
} }
b := w.Bytes() b := w.Bytes()
if !bytes.Equal(b, []byte(step.ExpectedContent)) { match, err := step.MatchesExpectedContent(b)
if err != nil {
t.Fatalf("Error compiling regex for step '%s': %v", step.Input, err)
}
if !match {
t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", step.ExpectedContent, b) t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", step.ExpectedContent, b)
} }
} }
@ -194,7 +208,7 @@ func TestMyAccount_Check_My_Balance(t *testing.T) {
} }
func TestMainMenuHelp(t *testing.T) { func TestMainMenuHelp(t *testing.T) {
en, fn := enginetest.TestEngine(sessionID) en, fn, _ := testutil.TestEngine(sessionID)
defer fn() defer fn()
ctx := context.Background() ctx := context.Background()
sessions := testData sessions := testData
@ -216,7 +230,11 @@ func TestMainMenuHelp(t *testing.T) {
} }
b := w.Bytes() b := w.Bytes()
if !bytes.Equal(b, []byte(step.ExpectedContent)) { match, err := step.MatchesExpectedContent(b)
if err != nil {
t.Fatalf("Error compiling regex for step '%s': %v", step.Input, err)
}
if !match {
t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", step.ExpectedContent, b) t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", step.ExpectedContent, b)
} }
} }
@ -225,7 +243,7 @@ func TestMainMenuHelp(t *testing.T) {
} }
func TestMainMenuQuit(t *testing.T) { func TestMainMenuQuit(t *testing.T) {
en, fn := enginetest.TestEngine(sessionID) en, fn, _ := testutil.TestEngine(sessionID)
defer fn() defer fn()
ctx := context.Background() ctx := context.Background()
sessions := testData sessions := testData
@ -247,7 +265,11 @@ func TestMainMenuQuit(t *testing.T) {
} }
b := w.Bytes() b := w.Bytes()
if !bytes.Equal(b, []byte(step.ExpectedContent)) { match, err := step.MatchesExpectedContent(b)
if err != nil {
t.Fatalf("Error compiling regex for step '%s': %v", step.Input, err)
}
if !match {
t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", step.ExpectedContent, b) t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", step.ExpectedContent, b)
} }
} }
@ -256,7 +278,7 @@ func TestMainMenuQuit(t *testing.T) {
} }
func TestMyAccount_Check_Community_Balance(t *testing.T) { func TestMyAccount_Check_Community_Balance(t *testing.T) {
en, fn := enginetest.TestEngine(sessionID) en, fn, _ := testutil.TestEngine(sessionID)
defer fn() defer fn()
ctx := context.Background() ctx := context.Background()
sessions := testData sessions := testData
@ -278,7 +300,11 @@ func TestMyAccount_Check_Community_Balance(t *testing.T) {
t.Errorf("Test case '%s' failed during Flush: %v", group.Name, err) t.Errorf("Test case '%s' failed during Flush: %v", group.Name, err)
} }
b := w.Bytes() b := w.Bytes()
if !bytes.Equal(b, []byte(step.ExpectedContent)) { match, err := step.MatchesExpectedContent(b)
if err != nil {
t.Fatalf("Error compiling regex for step '%s': %v", step.Input, err)
}
if !match {
t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", step.ExpectedContent, b) t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", step.ExpectedContent, b)
} }
} }
@ -287,7 +313,7 @@ func TestMyAccount_Check_Community_Balance(t *testing.T) {
} }
func TestMyAccount_MyAddress(t *testing.T) { func TestMyAccount_MyAddress(t *testing.T) {
en, fn := enginetest.TestEngine(sessionID) en, fn, _ := testutil.TestEngine(sessionID)
defer fn() defer fn()
ctx := context.Background() ctx := context.Background()
sessions := testData sessions := testData
@ -311,10 +337,12 @@ func TestMyAccount_MyAddress(t *testing.T) {
b := w.Bytes() b := w.Bytes()
publicKey := extractPublicKey(b) publicKey := extractPublicKey(b)
expectedContent := bytes.Replace([]byte(step.ExpectedContent), []byte("{public_key}"), []byte(publicKey), -1) expectedContent := bytes.Replace([]byte(step.ExpectedContent), []byte("{public_key}"), []byte(publicKey), -1)
match, err := step.MatchesExpectedContent(b)
if !bytes.Equal(b, expectedContent) { if err != nil {
t.Fatalf("Error compiling regex for step '%s': %v", step.Input, err)
}
if !match {
t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", expectedContent, b) t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", expectedContent, b)
} }
} }
@ -327,7 +355,7 @@ func TestGroups(t *testing.T) {
if err != nil { if err != nil {
log.Fatalf("Failed to load test groups: %v", err) log.Fatalf("Failed to load test groups: %v", err)
} }
en, fn := enginetest.TestEngine(sessionID) en, fn, _ := testutil.TestEngine(sessionID)
defer fn() defer fn()
ctx := context.Background() ctx := context.Background()
// Create test cases from loaded groups // Create test cases from loaded groups
@ -347,7 +375,11 @@ func TestGroups(t *testing.T) {
t.Errorf("Test case '%s' failed during Flush: %v", tt.Name, err) t.Errorf("Test case '%s' failed during Flush: %v", tt.Name, err)
} }
b := w.Bytes() b := w.Bytes()
if !bytes.Equal(b, []byte(tt.ExpectedContent)) { match, err := tt.MatchesExpectedContent(b)
if err != nil {
t.Fatalf("Error compiling regex for step '%s': %v", tt.Input, err)
}
if !match {
t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", tt.ExpectedContent, b) t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", tt.ExpectedContent, b)
} }