package file import ( "fmt" "os" "reflect" "testing" "git.andreafazzi.eu/andrea/probo/client" "git.andreafazzi.eu/andrea/probo/hasher/sha256" "git.andreafazzi.eu/andrea/probo/models" "git.andreafazzi.eu/andrea/probo/store/memory" "github.com/remogatto/prettytest" ) var testdataDir = "./testdata" type quizTestSuite struct { prettytest.Suite } func TestRunner(t *testing.T) { prettytest.Run( t, new(quizTestSuite), new(collectionTestSuite), ) } func (t *quizTestSuite) TestQuizFromMarkdown() { markdown := `Question text (1). Question text (2). Question text with #tag1 #tag2 (3). * Answer 1 * Answer 2 * Answer 3 * Answer 4` expectedQuiz := &client.Quiz{ Question: &client.Question{Text: "Question text (1).\n\nQuestion text (2).\n\nQuestion text with #tag1 #tag2 (3)."}, Answers: []*client.Answer{ {Text: "Answer 1", Correct: true}, {Text: "Answer 2", Correct: false}, {Text: "Answer 3", Correct: false}, {Text: "Answer 4", Correct: false}, }, } quiz, _, err := QuizFromMarkdown(markdown) t.Nil(err, fmt.Sprintf("Quiz should be parsed without errors: %v", err)) if !t.Failed() { t.True(reflect.DeepEqual(quiz, expectedQuiz), fmt.Sprintf("Expected %+v, got %+v", expectedQuiz, quiz)) } } func (t *quizTestSuite) TestReadAllQuizzes() { store, err := NewFileProboCollectorStore("./testdata/") t.True(err == nil, fmt.Sprintf("A file store should be initialized without problems but an error occurred: %v", err)) if !t.Failed() { result, err := store.ReadAllQuizzes() t.True(err == nil, fmt.Sprintf("Quizzes should be returned without errors: %v", err)) if !t.Failed() { t.Equal( 4, len(result), fmt.Sprintf("The store contains 5 files but only 4 should be parsed (duplicated quiz). Total of parsed quizzes are instead %v", len(result)), ) } } } func (t *quizTestSuite) TestMarkdownFromQuiz() { store := memory.NewMemoryProboCollectorStore(sha256.NewDefault256Hasher(sha256.DefaultSHA256HashingFn)) quiz, err := store.CreateQuiz( &client.CreateUpdateQuizRequest{ Quiz: &client.Quiz{ Question: &client.Question{Text: "Newly created question text."}, Answers: []*client.Answer{ {Text: "Answer 1", Correct: true}, {Text: "Answer 2", Correct: false}, {Text: "Answer 3", Correct: false}, {Text: "Answer 4", Correct: false}, }, }, }) md, err := MarkdownFromQuiz(quiz) t.Nil(err, "Conversion to markdown should not raise an error") if !t.Failed() { t.Equal(`Newly created question text. * Answer 1 * Answer 2 * Answer 3 * Answer 4 `, md) } } func (t *quizTestSuite) TestCreateQuiz() { store, err := NewFileProboCollectorStore(testdataDir) t.True(err == nil, fmt.Sprintf("A file store should be initialized without problems but an error occurred: %v", err)) if !t.Failed() { clientQuiz := &client.Quiz{ Question: &client.Question{Text: "Newly created question text."}, Answers: []*client.Answer{ {Text: "Answer 1", Correct: true}, {Text: "Answer 2", Correct: false}, {Text: "Answer 3", Correct: false}, {Text: "Answer 4", Correct: false}, }, } quiz, err := store.CreateQuiz( &client.CreateUpdateQuizRequest{ Quiz: clientQuiz, }, ) t.Nil(err, fmt.Sprintf("An error was raised when saving the quiz on disk: %v", err)) if !t.Failed() { path, err := store.GetQuizPath(quiz) t.Nil(err, "GetPath should not raise an error.") if !t.Failed() { exists, err := os.Stat(path) t.Nil(err, "Stat should not return an error") if !t.Failed() { t.True(exists != nil, "The new quiz file was not created.") if !t.Failed() { quizFromDisk, _, err := readQuizFromDisk(path) t.Nil(err, "Quiz should be read from disk without errors.") if !t.Failed() { t.True(reflect.DeepEqual(quizFromDisk, clientQuiz), "Quiz read from disk and stored in memory should be equal.") err := os.Remove(path) t.Nil(err, "Test file should be removed without errors.") } } } } } } } func (t *quizTestSuite) TestDeleteQuiz() { store, err := NewFileProboCollectorStore(testdataDir) t.True(err == nil, fmt.Sprintf("A file store should be initialized without problems but an error occurred: %v", err)) if !t.Failed() { quiz, err := createQuizOnDisk(store, &client.CreateUpdateQuizRequest{ Quiz: &client.Quiz{ Question: &client.Question{Text: "This quiz should be deleted."}, Answers: []*client.Answer{ {Text: "Answer 1", Correct: true}, {Text: "Answer 2", Correct: false}, {Text: "Answer 3", Correct: false}, {Text: "Answer 4", Correct: false}, }, }, }) t.Nil(err, "The quiz to be deleted should be created without issue") path, err := store.GetQuizPath(quiz) t.True(path != "", "Quiz path should be obtained without errors") if !t.Failed() { deletedQuiz, err := store.DeleteQuiz(&client.DeleteQuizRequest{ID: quiz.ID}) t.Nil(err, fmt.Sprintf("Quiz should be deleted without errors: %v", err)) t.True(reflect.DeepEqual(quiz, deletedQuiz), "Quiz should be updateEd.") } } } func (t *quizTestSuite) TestUpdateQuiz() { store, err := NewFileProboCollectorStore(testdataDir) t.True(err == nil, fmt.Sprintf("A file store should be initialized without problems but an error occurred: %v", err)) if !t.Failed() { quiz, err := createQuizOnDisk(store, &client.CreateUpdateQuizRequest{ Quiz: &client.Quiz{ Question: &client.Question{Text: "Newly created question text."}, Answers: []*client.Answer{ {Text: "Answer 1", Correct: true}, {Text: "Answer 2", Correct: false}, {Text: "Answer 3", Correct: false}, {Text: "Answer 4", Correct: false}, }, }, }) t.Nil(err, "The quiz to be updated should be created without issue") if !t.Failed() { clientQuiz := &client.Quiz{ Question: &client.Question{Text: "Updated question text with #tag."}, Answers: []*client.Answer{ {Text: "Answer 1", Correct: true}, {Text: "Answer 2", Correct: false}, {Text: "Answer 3", Correct: false}, {Text: "Answer 4", Correct: false}, }, } updatedQuiz, err := store.UpdateQuiz( &client.CreateUpdateQuizRequest{ Quiz: clientQuiz, }, quiz.ID) t.Nil(err, fmt.Sprintf("Quiz should be updated without errors: %v", err)) t.Equal(updatedQuiz.ID, quiz.ID, fmt.Sprintf("IDs should remain the same after an update: updated ID %v original ID %v", updatedQuiz.ID, quiz.ID)) t.True(len(updatedQuiz.Tags) == 1, "Length of tags array should be 1") if !t.Failed() { path, err := store.GetQuizPath(updatedQuiz) if !t.Failed() { t.Nil(err, "GetPath should not raise an error.") if !t.Failed() { quizFromDisk, _, err := readQuizFromDisk(path) t.Nil(err, "Quiz should be read from disk without errors.") if !t.Failed() { t.True(reflect.DeepEqual(clientQuiz, quizFromDisk), "Quiz should be updated.") err := os.Remove(path) t.Nil(err, "Stat should not return an error") } } } } } } } func (t *quizTestSuite) TestReadMetaHeaderFromFile() { store, err := NewFileProboCollectorStore(testdataDir) t.True(err == nil, fmt.Sprintf("A file store should be initialized without problems but an error occurred: %v", err)) meta, err := store.ReadMetaHeaderFromFile("quiz_4.md") t.True(err == nil, fmt.Sprintf("An error occurred: %v", err)) if !t.Failed() { t.True(meta.ID != "") t.True(meta.CreatedAt.String() != "") } } func (t *quizTestSuite) TestWriteMetaHeaderToFile() { store, err := NewFileProboCollectorStore(testdataDir) t.True(err == nil, fmt.Sprintf("A file store should be initialized without problems but an error occurred: %v", err)) if !t.Failed() { meta, err := store.ReadMetaHeaderFromFile("quiz_5.md") t.True(err == nil, fmt.Sprintf("Reading the header returns the following error: %v", err)) if !t.Failed() { t.True(meta != nil, "Meta header should not be nil") if !t.Failed() { t.True(meta.ID != "", "ID should not be empty") if !t.Failed() { _, err = store.removeMetaFromFile("quiz_5.md") t.True(err == nil) } } } } } func createQuizOnDisk(store *FileProboCollectorStore, req *client.CreateUpdateQuizRequest) (*models.Quiz, error) { return store.CreateQuiz(req) } func readQuizFromDisk(path string) (*client.Quiz, *models.Meta, error) { content, err := os.ReadFile(path) if err != nil { return nil, nil, err } return QuizFromMarkdown(string(content)) } func testsAreEqual(got, want []*models.Quiz) bool { return reflect.DeepEqual(got, want) }