package repository_test import ( "testing" "testing/synctest" "context" "fmt" "strings" "time" "git.hrafn.xyz/aether/gotes/internal/models" "git.hrafn.xyz/aether/gotes/internal/repository" ) var fixedTestDate = time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC) //Date is set by synctest to this specific time func TestCreateNote(t *testing.T) { testCases := []struct { name string title string content string expectedNote models.Note expectedError bool }{ { name: "Good note", title: "Good", content: "This is a good note with valid content.", expectedNote: models.Note{ ID: 1, LastUpdate: fixedTestDate, Title: "Good", Content: "This is a good note with valid content.", }, expectedError: false, }, { name: "Empty content", title: "Empty", content: "", expectedNote: models.Note{}, expectedError: true, }, { name: "Long content", title: "Long", content: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", expectedNote: models.Note{ ID: 2, LastUpdate: fixedTestDate, Title: "Long", Content: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", }, expectedError: false, }, { name: "Empty title", title: "", content: "Some content sure", expectedNote: models.Note{}, expectedError: true, }, { name: "Fully empty", title: "", content: "", expectedNote: models.Note{}, expectedError: true, }, { name: "Title too long", title: strings.Repeat("A", models.NoteTitleMaxLength+1), content: "", expectedNote: models.Note{}, expectedError: true, }, { name: "Title accept max", title: strings.Repeat("U", models.NoteTitleMaxLength), content: `That's a whole lot of "U"s`, expectedNote: models.Note{ ID: 3, LastUpdate: fixedTestDate, Title: strings.Repeat("U", models.NoteTitleMaxLength), Content: `That's a whole lot of "U"s`, }, expectedError: false, }, } repo := repository.NewNoteRepository(&mockNoteStore{}) for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { synctest.Test(t, func(t *testing.T) { note, err := repo.CreateNote(context.Background(), tc.title, tc.content) if tc.expectedError { if err == nil { t.Errorf("expected an error but got none") } } else { if err != nil { t.Errorf("unexpected error: %v", err) } } if note.Content != tc.expectedNote.Content { t.Errorf("expected content %q but got %q", tc.expectedNote.Content, note.Content) } if note.Title != tc.expectedNote.Title { t.Errorf("expected title %q but got %q", tc.expectedNote.Title, note.Title) } if note.ID != tc.expectedNote.ID { t.Errorf("expected ID %d but got %d", tc.expectedNote.ID, note.ID) } if !note.LastUpdate.Equal(tc.expectedNote.LastUpdate) { t.Errorf("expected LastUpdate %v but got %v", tc.expectedNote.LastUpdate, note.LastUpdate) } fmt.Printf("Test case '%s' passed.\n", tc.name) }) }) } } func TestGetNotes(t *testing.T) { notes := []models.Note{ {ID: 1, LastUpdate: fixedTestDate, Content: "First note", Title: "One"}, {ID: 2, LastUpdate: fixedTestDate, Content: "Second note", Title: "Two"}, {ID: 3, LastUpdate: fixedTestDate, Content: "Third note", Title: "Three"}, // 4th note was clearly deleted, :sadface: {ID: 5, LastUpdate: fixedTestDate, Content: "Lorem ipsum note dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", Title: "Lorem"}, } store := &mockNoteStore{Notes: notes} repo := repository.NewNoteRepository(store) testCases := []struct { name string id int expectedNote models.Note expectedError bool }{ { name: "Existing note", id: 1, expectedNote: notes[0], expectedError: false, }, { name: "Non-existing note", id: 999, expectedNote: models.Note{}, expectedError: true, }, { name: "Secret missing 4th note", id: 4, expectedNote: models.Note{}, expectedError: true, }, { name: "That long lorem ipsum note", id: 5, expectedNote: notes[3], expectedError: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { note, err := repo.GetNote(context.Background(), tc.id) if tc.expectedError { if err == nil { t.Errorf("expected an error but got none") } } else { if err != nil { t.Errorf("unexpected error: %v", err) } } if note.Content != tc.expectedNote.Content { t.Errorf("expected content %q but got %q", tc.expectedNote.Content, note.Content) } if note.ID != tc.expectedNote.ID { t.Errorf("expected ID %d but got %d", tc.expectedNote.ID, note.ID) } // Purposefully not checking time as it doesn't make sense in context }) } } func TestListNotes(t *testing.T) { testcases := []struct { name string notes []models.Note expectedNotes []models.Note expectedError bool }{ { name: "No notes", notes: []models.Note{}, expectedNotes: []models.Note{}, expectedError: false, }, { name: "Multiple notes", notes: []models.Note{ {ID: 1, LastUpdate: fixedTestDate, Content: "First note", Title: "Seven"}, {ID: 2, LastUpdate: fixedTestDate, Content: "Second note", Title: "Nine"}, {ID: 3, LastUpdate: fixedTestDate, Content: "Third note", Title: "FourtyThree"}, }, expectedNotes: []models.Note{ {ID: 1, LastUpdate: fixedTestDate, Content: "First note", Title: "Seven"}, {ID: 2, LastUpdate: fixedTestDate, Content: "Second note", Title: "Nine"}, {ID: 3, LastUpdate: fixedTestDate, Content: "Third note", Title: "FourtyThree"}, }, expectedError: false, }, } for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { store := &mockNoteStore{Notes: tc.notes} repo := repository.NewNoteRepository(store) notes, err := repo.ListNotes(context.Background()) if tc.expectedError { if err == nil { t.Errorf("expected an error but got none") } } else { if err != nil { t.Errorf("unexpected error: %v", err) } } if len(notes) != len(tc.expectedNotes) { t.Errorf("expected %d notes but got %d", len(tc.expectedNotes), len(notes)) } for i, note := range notes { if note.Content != tc.expectedNotes[i].Content { t.Errorf("expected content %q but got %q", tc.expectedNotes[i].Content, note.Content) } if note.Title != tc.expectedNotes[i].Title { t.Errorf("expected title %q but got %q", tc.expectedNotes[i].Title, note.Title) } if note.ID != tc.expectedNotes[i].ID { t.Errorf("expected ID %d but got %d", tc.expectedNotes[i].ID, note.ID) } // Purposefully not checking time as it doesn't make sense in context } }) } } func TestUpdateNote(t *testing.T) { notes := []models.Note{ {ID: 1, LastUpdate: time.Date(1984, 11, 20, 0, 0, 0, 0, time.UTC), Content: "First note", Title: "Joe"}, {ID: 2, LastUpdate: time.Date(2018, 6, 8, 0, 0, 0, 0, time.UTC), Content: "Second note", Title: "Momma"}, {ID: 3, LastUpdate: time.Date(2018, 6, 8, 0, 0, 0, 0, time.UTC), Content: "Third note", Title: "So"}, {ID: 4, LastUpdate: time.Date(2018, 6, 8, 0, 0, 0, 0, time.UTC), Content: "Four note", Title: "Lovely"}, {ID: 5, LastUpdate: time.Date(2018, 6, 8, 0, 0, 0, 0, time.UTC), Content: "Fifth note", Title: "No, really"}, {ID: 6, LastUpdate: time.Date(2018, 6, 8, 0, 0, 0, 0, time.UTC), Content: "six note", Title: "She is!"}, } store := &mockNoteStore{Notes: notes} repo := repository.NewNoteRepository(store) testcases := []struct { name string id int title string content string expectedNote models.Note expectedError bool }{ { name: "Update existing note content only", id: 1, title: "", content: "Updated first note", expectedNote: models.Note{ ID: 1, LastUpdate: fixedTestDate, Content: "Updated first note", Title: "Joe", }, expectedError: false, }, { name: "Update non-existing note", id: 999, title: "Does it matter?", content: "This note does not exist", expectedNote: models.Note{}, expectedError: true, }, { name: "Update with empty content and title", id: 2, content: "", title: "", expectedNote: models.Note{}, expectedError: true, }, { name: "Update with unchanged content", id: 3, content: "Third note", title: "So", expectedNote: models.Note{}, expectedError: true, }, { name: "Update with title only", id: 4, content: "", title: "Davey crocket", expectedNote: models.Note{ ID: 4, LastUpdate: fixedTestDate, Content: "Four note", Title: "Davey crocket", }, expectedError: false, }, { name: "Update with content only", id: 5, content: "Just internal screaming", title: "", expectedNote: models.Note{ ID: 5, LastUpdate: fixedTestDate, Content: "Just internal screaming", Title: "No, really", }, expectedError: false, }, { name: "Update title too long", id: 6, content: "Kinda irrelavant, right?", title: strings.Repeat("F", models.NoteTitleMaxLength+1), expectedNote: models.Note{}, expectedError: true, }, } for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { synctest.Test(t, func(t *testing.T) { note, err := repo.UpdateNote(context.Background(), tc.id, repository.NoteUpdate{ Content: tc.content, Title: tc.title, }) if tc.expectedError { if err == nil { t.Errorf("expected an error but got none") } } else { if err != nil { t.Errorf("unexpected error: %v", err) } } if note.Content != tc.expectedNote.Content { t.Errorf("expected content %q but got %q", tc.expectedNote.Content, note.Content) } if note.Title != tc.expectedNote.Title { t.Errorf("expected title %q but got %q", tc.expectedNote.Title, note.Title) } if note.ID != tc.expectedNote.ID { t.Errorf("expected ID %d but got %d", tc.expectedNote.ID, note.ID) } if !note.LastUpdate.Equal(tc.expectedNote.LastUpdate) { t.Errorf("expected LastUpdate %v but got %v", tc.expectedNote.LastUpdate, note.LastUpdate) } fmt.Printf("Test case '%s' passed.\n", tc.name) }) }) } } func TestDeleteNote(t *testing.T) { testcases := []struct { name string id int setupNotes []models.Note expectedNotes []models.Note expectedError bool }{ { name: "Delete existing note", id: 1, setupNotes: []models.Note{ {ID: 1, LastUpdate: fixedTestDate, Content: "First note"}, {ID: 2, LastUpdate: fixedTestDate, Content: "Second note"}, }, expectedNotes: []models.Note{ {ID: 2, LastUpdate: fixedTestDate, Content: "Second note"}, }, expectedError: false, }, { name: "Delete non-existing note", id: 999, setupNotes: []models.Note{ {ID: 1, LastUpdate: fixedTestDate, Content: "First note"}, {ID: 2, LastUpdate: fixedTestDate, Content: "Second note"}, }, expectedNotes: []models.Note{ {ID: 1, LastUpdate: fixedTestDate, Content: "First note"}, {ID: 2, LastUpdate: fixedTestDate, Content: "Second note"}, }, expectedError: true, }, } for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { store := &mockNoteStore{Notes: tc.setupNotes} repo := repository.NewNoteRepository(store) err := repo.DeleteNote(context.Background(), tc.id) if tc.expectedError { if err == nil { t.Errorf("expected an error but got none") } } else { if err != nil { t.Errorf("unexpected error: %v", err) } } notes, _ := repo.ListNotes(context.Background()) if len(notes) != len(tc.expectedNotes) { t.Errorf("expected %d notes but got %d", len(tc.expectedNotes), len(notes)) } for i, note := range notes { if len(tc.expectedNotes) <= i { t.Errorf("unexpected extra note: %v", note) continue } if note.Content != tc.expectedNotes[i].Content { t.Errorf("expected content %q but got %q", tc.expectedNotes[i].Content, note.Content) } if note.ID != tc.expectedNotes[i].ID { t.Errorf("expected ID %d but got %d", tc.expectedNotes[i].ID, note.ID) } // Purposefully not checking time as it doesn't make sense in context } }) } }