From 191d2a460bad0b7ca3d3e0198aedeecd4557e2c7 Mon Sep 17 00:00:00 2001 From: Juho9 Date: Thu, 16 Nov 2023 16:21:36 +0200 Subject: [PATCH 01/26] New function names to queries and mocks, added reservation tests --- src/queries/__mocks__/book.ts | 24 +- src/queries/__mocks__/book_list.ts | 12 +- src/queries/__mocks__/book_list_entry.ts | 10 +- src/queries/__mocks__/book_reservation.ts | 281 ++++++++++++++++++++++ src/queries/__mocks__/borrow.ts | 30 +-- src/queries/__mocks__/session.ts | 8 +- src/queries/__mocks__/user.ts | 26 +- test/routes/book.test.ts | 29 ++- test/routes/book_list.test.ts | 1 + test/routes/book_list_entry.test.ts | 1 + test/routes/book_reservation.test.ts | 83 +++++++ test/routes/borrow.test.ts | 1 + 12 files changed, 441 insertions(+), 65 deletions(-) create mode 100644 src/queries/__mocks__/book_reservation.ts create mode 100644 test/routes/book_reservation.test.ts diff --git a/src/queries/__mocks__/book.ts b/src/queries/__mocks__/book.ts index 5fd7438..26f66f5 100644 --- a/src/queries/__mocks__/book.ts +++ b/src/queries/__mocks__/book.ts @@ -62,7 +62,7 @@ let book4: Book = { deleted: false, } -let mockBookData = [book1, book2, book3, book4] +export let mockBookData = [book1, book2, book3, book4] let idCounter = 4 const getBook = (id: number) => { @@ -74,11 +74,11 @@ const getBook = (id: number) => { return null } -export const querySelectBook = async (bookId: number): Promise => { +export const getBookById = async (bookId: number): Promise => { return getBook(bookId) } -export const querySelectAllBooks = async (): Promise => { +export const getAllExistingBooks = async (): Promise => { let array: Array = [] mockBookData.forEach((element) => { if (!element.deleted) { @@ -87,11 +87,19 @@ export const querySelectAllBooks = async (): Promise => { }) return array as Array } -export const querySelectAllExistingBooks = async (): Promise => { +export const getAllBooks = async (): Promise => { return mockBookData as Array } -export const queryHardDeleteBook = async (bookId: number): Promise => { +export const getCountOfAllBooks = async (): Promise => { + let count = 0 + for (let index = 0; index < mockBookData.length; index++) { + count++ + } + return count +} + +export const deleteBook = async (bookId: number): Promise => { let deleted = false mockBookData = mockBookData.filter((book) => { if (book.id == bookId) deleted = true @@ -100,7 +108,7 @@ export const queryHardDeleteBook = async (bookId: number): Promise => { return deleted } -export const querySoftDeleteBook = async (bookId: number): Promise => { +export const markBookAsDeleted = async (bookId: number): Promise => { const book = getBook(bookId) if (book) { book.deleted = true @@ -109,7 +117,7 @@ export const querySoftDeleteBook = async (bookId: number): Promise => { return false } -export const queryInsertBook = async ( +export const insertNewBook = async ( userId: number, title: string, image: string, @@ -138,7 +146,7 @@ export const queryInsertBook = async ( return true } -export const queryUpdateBook = async (book: Book): Promise => { +export const updateBook = async (book: Book): Promise => { const editedBook = getBook(book.id) if (editedBook) { editedBook.title = book.title diff --git a/src/queries/__mocks__/book_list.ts b/src/queries/__mocks__/book_list.ts index d9cf758..8e03c66 100644 --- a/src/queries/__mocks__/book_list.ts +++ b/src/queries/__mocks__/book_list.ts @@ -32,11 +32,11 @@ const getBook_list = (id: number) => { return null } -export const querySelectAllLists = async () => { +export const getAllLists = async () => { return mockBook_listData as Array } -export const querySelectListByUser = async (userId: number) => { +export const getListsByUser = async (userId: number) => { let array: Array = [] mockBook_listData.forEach((element) => { if (element.user == userId) { @@ -46,11 +46,11 @@ export const querySelectListByUser = async (userId: number) => { return array as Array } -export const querySelectList = async (listId: number) => { +export const getListById = async (listId: number) => { return getBook_list(listId) } -export const queryInsertNewList = async ( +export const insertNewList = async ( userId: number, listName: string ): Promise => { @@ -62,7 +62,7 @@ export const queryInsertNewList = async ( return true } -export const queryDeleteList = async (listId: number) => { +export const deleteList = async (listId: number) => { let deleted = false mockBook_listData = mockBook_listData.filter((book_list) => { if (book_list.id == listId) deleted = true @@ -71,7 +71,7 @@ export const queryDeleteList = async (listId: number) => { return deleted } -export const queryUpdateList = async (book_list: Book_list) => { +export const updateList = async (book_list: Book_list) => { const editedBook_list = getBook_list(book_list.id) if (editedBook_list) { editedBook_list.name = book_list.name diff --git a/src/queries/__mocks__/book_list_entry.ts b/src/queries/__mocks__/book_list_entry.ts index 5996f17..e71f9e9 100644 --- a/src/queries/__mocks__/book_list_entry.ts +++ b/src/queries/__mocks__/book_list_entry.ts @@ -36,11 +36,11 @@ const getBook_list_entry = (id: number) => { return null } -export const querySelectAllEntries = async () => { +export const getAllEntries = async () => { return mockBook_list_entryData as Array } -export const querySelectAllEntriesByList = async (listId: number) => { +export const getEntriesByList = async (listId: number) => { let array: Array = [] mockBook_list_entryData.forEach((element) => { if (element.list == listId) array.push(element) @@ -48,11 +48,11 @@ export const querySelectAllEntriesByList = async (listId: number) => { return array as Array } -export const querySelectEntry = async (entryId: number) => { +export const getEntryById = async (entryId: number) => { return getBook_list_entry(entryId) } -export const queryInsertEntry = async (book_list_entry: Book_list_entry) => { +export const insertNewEntry = async (book_list_entry: Book_list_entry) => { mockBook_list_entryData.push({ id: idCounter++, list: book_list_entry.list, @@ -61,7 +61,7 @@ export const queryInsertEntry = async (book_list_entry: Book_list_entry) => { return true } -export const queryRemoveFromList = async (entryId: number) => { +export const removeEntryById = async (entryId: number) => { let deleted = false mockBook_list_entryData = mockBook_list_entryData.filter( (book_list_entry) => { diff --git a/src/queries/__mocks__/book_reservation.ts b/src/queries/__mocks__/book_reservation.ts new file mode 100644 index 0000000..d110612 --- /dev/null +++ b/src/queries/__mocks__/book_reservation.ts @@ -0,0 +1,281 @@ +import Book_reservation from '../../interfaces/book_reservation.interface' +import ExtendedReservation from '../../interfaces/extendedReservation.interface' + +import { mockUserData } from './user' +import { mockBookData } from './book' +import { mockBorrowData, isBookAvailable } from './borrow' +import { RESERVATION_DAYS, MS_IN_DAY } from '../../constants' + +// note: this mock is very basic and its purpose is for jest to run through routes + +let date = new Date() + +let reservation1: Book_reservation = { + id: 1, + bookId: 1, + userId: 1, + borrowId: 1, + reservationDatetime: date, + loaned: false, + canceled: false, +} + +let reservation2: Book_reservation = { + id: 2, + bookId: 2, + userId: 2, + borrowId: 2, + reservationDatetime: date, + loaned: true, + canceled: false, +} + +let reservation3: Book_reservation = { + id: 3, + bookId: 3, + userId: 3, + borrowId: 3, + reservationDatetime: date, + loaned: false, + canceled: false, +} + +let reservation4: Book_reservation = { + id: 4, + bookId: 1, + userId: 2, + borrowId: 4, + reservationDatetime: date, + loaned: false, + canceled: false, +} + +let reservation5: Book_reservation = { + id: 5, + bookId: 2, + userId: 3, + borrowId: 5, + reservationDatetime: date, + loaned: false, + canceled: false, +} + +let mockReservationsData = [ + reservation1, + reservation2, + reservation3, + reservation4, + reservation5, +] +let idCount = 5 + +const getUsername = async (userId: number): Promise => { + for (let index = 0; index < mockUserData.length; index++) { + if (mockUserData[index].id === userId) { + return mockUserData[index].username + } + } + return null +} + +const getBookTitle = async (bookId: number): Promise => { + for (let index = 0; index < mockBookData.length; index++) { + if (mockBookData[index].id === bookId) { + return mockBookData[index].title + } + } + return null +} + +const getReturnDate = async (borrowId: number): Promise => { + for (let index = 0; index < mockBorrowData.length; index++) { + if (mockBorrowData[index].id === borrowId) { + return mockBorrowData[index].returnDate + } + } + return null +} + +const filterValidReservations = (reservations: any) => { + return JSON.parse(JSON.stringify(reservations)).filter( + (reservation: ExtendedReservation) => + reservation.returnDate === null || + new Date( + new Date(reservation.returnDate).getTime() + + RESERVATION_DAYS * MS_IN_DAY + ).getTime() > new Date().getTime() + ) +} + +///bookreservation/all (GET) +export const getAllReservations = async (): Promise => { + let array: Array = [] + mockReservationsData.forEach((element) => { + array.push(element) + }) + return array +} + +///bookreservation/all/current +export const getCurrentReservations = async (): Promise< + ExtendedReservation[] +> => { + const array: Array = [] + for (let index = 0; index < mockReservationsData.length; index++) { + if ( + mockReservationsData[index].canceled !== true && + mockReservationsData[index].loaned !== true + ) { + let username = await getUsername(mockReservationsData[index].userId) + let title = await getBookTitle(mockReservationsData[index].bookId) + let returnDate = await getReturnDate(mockReservationsData[index].borrowId) + if (username !== null && title !== null) { + array.push({ + id: mockReservationsData[index].id, + username: username, + userId: mockReservationsData[index].userId, + title: title, + bookId: mockReservationsData[index].bookId, + reservationDatetime: mockReservationsData[index].reservationDatetime, + loaned: mockReservationsData[index].loaned, + canceled: mockReservationsData[index].canceled, + returnDate: returnDate, + }) + } + } + } + return filterValidReservations(array) as ExtendedReservation[] +} + +///bookreservation/all/extended +export const getAllExtendedReservations = async (): Promise< + ExtendedReservation[] +> => { + const array: Array = [] + for (let index = 0; index < mockReservationsData.length; index++) { + let username = await getUsername(mockReservationsData[index].userId) + let title = await getBookTitle(mockReservationsData[index].bookId) + let returnDate = await getReturnDate(mockReservationsData[index].borrowId) + if (username !== null && title !== null) { + array.push({ + id: mockReservationsData[index].id, + username: username, + userId: mockReservationsData[index].userId, + title: title, + bookId: mockReservationsData[index].bookId, + reservationDatetime: mockReservationsData[index].reservationDatetime, + loaned: mockReservationsData[index].loaned, + canceled: mockReservationsData[index].canceled, + returnDate: returnDate, + }) + } + } + return filterValidReservations(array) as ExtendedReservation[] +} + +///bookreservation/book (GET) +export const getCurrentReservationForBook = async (bookId: number) => { + const array: Array = [] + for (let index = 0; index < mockReservationsData.length; index++) { + if (mockReservationsData[index].bookId === bookId) { + let username = await getUsername(mockReservationsData[index].userId) + let title = await getBookTitle(mockReservationsData[index].bookId) + let returnDate = await getReturnDate(mockReservationsData[index].borrowId) + if (username !== null && title !== null) { + array.push({ + id: mockReservationsData[index].id, + username: username, + userId: mockReservationsData[index].userId, + title: title, + bookId: mockReservationsData[index].bookId, + reservationDatetime: mockReservationsData[index].reservationDatetime, + loaned: mockReservationsData[index].loaned, + canceled: mockReservationsData[index].canceled, + returnDate: returnDate, + }) + } + } + } + const validReservations = filterValidReservations(array) + return validReservations.length > 0 + ? (validReservations[0] as ExtendedReservation) + : null +} + +export const getUserCurrentExtendedReservations = async ( + userId: number +): Promise => { + const array: Array = [] + for (let index = 0; index < mockReservationsData.length; index++) { + if ( + mockReservationsData[index].bookId === userId && + mockReservationsData[index].loaned === false && + mockReservationsData[index].canceled === false + ) { + let username = await getUsername(mockReservationsData[index].userId) + let title = await getBookTitle(mockReservationsData[index].bookId) + let returnDate = await getReturnDate(mockReservationsData[index].borrowId) + if (username !== null && title !== null) { + array.push({ + id: mockReservationsData[index].id, + username: username, + userId: mockReservationsData[index].userId, + title: title, + bookId: mockReservationsData[index].bookId, + reservationDatetime: mockReservationsData[index].reservationDatetime, + loaned: mockReservationsData[index].loaned, + canceled: mockReservationsData[index].canceled, + returnDate: returnDate, + }) + } + } + } + const validReservations = filterValidReservations(array) + return validReservations.length > 0 + ? (validReservations as ExtendedReservation[]) + : null +} + +//Insert reservation +export const insertReservation = async ( + userId: number, + bookId: number +): Promise => { + const length = mockReservationsData.length + if (await getCurrentReservationForBook(bookId)) { + return false + } + const borrow = await isBookAvailable(bookId) + if (borrow === true) { + mockReservationsData.push({ + id: idCount + 1, + bookId: bookId, + userId: userId, + borrowId: mockBorrowData.length + 1, + reservationDatetime: date, + loaned: false, + canceled: false, + }) + } + return true +} + +//Cancel reservation +export const cancelReservation = async (bookId: number): Promise => { + mockReservationsData.forEach((element) => { + if (element.bookId === bookId) { + element.canceled = !element.canceled + } + }) + return true +} + +// /bookreservation/loan +export const loanReservation = async (bookId: number): Promise => { + mockReservationsData.forEach((element) => { + if (element.bookId === bookId) { + element.loaned = !element.loaned + } + }) + return true +} diff --git a/src/queries/__mocks__/borrow.ts b/src/queries/__mocks__/borrow.ts index ffeaf20..bbae694 100644 --- a/src/queries/__mocks__/borrow.ts +++ b/src/queries/__mocks__/borrow.ts @@ -56,7 +56,7 @@ const detailedExpiredBorrow1: DetailedExpiredBorrow = { userId: borrow4.library_user, } -let mockBorrowData = [borrow1, borrow2, borrow3, borrow4] +export let mockBorrowData = [borrow1, borrow2, borrow3, borrow4] let mockDetailedExpiredBorrowData = [detailedExpiredBorrow1] let idCounter = 4 @@ -69,11 +69,11 @@ const getBorrow = (id: number) => { return null } -export const querySelectAllBorrows = async (): Promise => { +export const getAllBorrows = async (): Promise => { return mockBorrowData as Array } -export const querySelectAllCurrentBorrows = async (): Promise => { +export const getAllCurrentBorrows = async (): Promise => { let array: Array = [] mockBorrowData.forEach((element) => { if (!element.returned) { @@ -83,7 +83,7 @@ export const querySelectAllCurrentBorrows = async (): Promise => { return array as Array } -export const querySelectAllCurrentBorrows2 = async () => { +export const getAllCurrentDetailedBorrows = async () => { let array: { username: string title: string @@ -105,15 +105,13 @@ export const querySelectAllCurrentBorrows2 = async () => { return array } -export const querySelectBorrow = async ( +export const getBorrowById = async ( borrowingId: number ): Promise => { return getBorrow(borrowingId) } -export const queryDeleteBorrow = async ( - borrowingId: number -): Promise => { +export const deleteBorrow = async (borrowingId: number): Promise => { let deleted = false mockBorrowData = mockBorrowData.filter((borrow) => { if (borrow.id == borrowingId) deleted = true @@ -122,7 +120,7 @@ export const queryDeleteBorrow = async ( return deleted } -export const queryInsertBorrow = async ( +export const insertBorrow = async ( userId: number, bookId: number, dueDate: Date, @@ -140,7 +138,7 @@ export const queryInsertBorrow = async ( return true } -export const queryUpdateBorrow = async (borrow: Borrow): Promise => { +export const updateBorrow = async (borrow: Borrow): Promise => { const editedBorrow = getBorrow(borrow.id) if (editedBorrow) { editedBorrow.book = borrow.book @@ -152,9 +150,7 @@ export const queryUpdateBorrow = async (borrow: Borrow): Promise => { return false } -export const queryBookIsAvailable = async ( - bookId: number -): Promise => { +export const isBookAvailable = async (bookId: number): Promise => { const borrow = getBorrow(bookId) let availCheck = 0 if (borrow) { @@ -165,9 +161,7 @@ export const queryBookIsAvailable = async ( return availCheck == 0 } -export const queryBorrowsByUserId = async ( - userId: number -): Promise => { +export const getBorrowsByUserId = async (userId: number): Promise => { let array: Array = [] const borrow = getBorrow(userId) if (borrow) { @@ -178,7 +172,7 @@ export const queryBorrowsByUserId = async ( return array as Array } -export const queryExpiredBorrows = async (): Promise => { +export const getExpiredBorrows = async (): Promise => { let array: Array = [] mockBorrowData.forEach((element) => { if (!element.returned && new Date() > element.dueDate) { @@ -188,7 +182,7 @@ export const queryExpiredBorrows = async (): Promise => { return array as Array } -export const queryDetailedExpiredBorrows = async (): Promise< +export const getDetailedExpiredBorrows = async (): Promise< DetailedExpiredBorrow[] > => { return mockDetailedExpiredBorrowData as Array diff --git a/src/queries/__mocks__/session.ts b/src/queries/__mocks__/session.ts index 138b3b6..f84e65e 100644 --- a/src/queries/__mocks__/session.ts +++ b/src/queries/__mocks__/session.ts @@ -1,17 +1,15 @@ import Session from '../../interfaces/session.interface' -async function querySelectSessionBySecret( - secret: string -): Promise { +async function getSessionBySecret(secret: string): Promise { const currentTime = new Date().getTime() / 1000 const session: Session = { id: 1, userId: 1, secret: '123', - expires: currentTime + 1234, + expires: currentTime + 123467, invalidated: false, } return session.secret === secret ? session : null } -export { querySelectSessionBySecret } +export { getSessionBySecret } diff --git a/src/queries/__mocks__/user.ts b/src/queries/__mocks__/user.ts index dc7521b..bcd5ef5 100644 --- a/src/queries/__mocks__/user.ts +++ b/src/queries/__mocks__/user.ts @@ -7,7 +7,7 @@ const user1: User = { username: 't1', email: 't1@t.est', passw: 'p1', - administrator: false, + administrator: true, deleted: false, } const user2: User = { @@ -15,7 +15,7 @@ const user2: User = { username: 't2', email: 't2@t.est', passw: 'p2', - administrator: true, + administrator: false, deleted: false, } const user3: User = { @@ -26,9 +26,9 @@ const user3: User = { administrator: false, deleted: true, } -const mockUserData = [user1, user2, user3] +export const mockUserData = [user1, user2, user3] -export const querySelectAllExistingUsers = async (userId: string) => { +export const getAllActiveUsers = async () => { let array: Array = [] mockUserData.forEach((element) => { if (!element.deleted) { @@ -38,11 +38,11 @@ export const querySelectAllExistingUsers = async (userId: string) => { return array as Array } -export const querySelectAllUsers = async () => { +export const getAllUsers = async () => { return mockUserData as Array } -export const querySelectUser = async (userId: number) => { +export const getUserById = async (userId: number) => { for (let index = 0; index < mockUserData.length; index++) { if (mockUserData[index].id === userId) { return mockUserData[index] @@ -51,11 +51,11 @@ export const querySelectUser = async (userId: number) => { return null } -export const querySelectUserBySessionId = async (sessionId: number) => { - return mockUserData[1] +export const getUserBySessionId = async (sessionId: number) => { + return mockUserData[0] } -export const querySelectUserByUsername = async (username: string) => { +export const getUserByUsername = async (username: string) => { for (let index = 0; index < mockUserData.length; index++) { if (mockUserData[index].username === username) { return mockUserData[index] @@ -64,15 +64,15 @@ export const querySelectUserByUsername = async (username: string) => { return null } -export const queryHardDeleteUser = async (userId: string) => { +export const deleteUserHard = async (userId: string) => { return true } -export const querySoftDeleteUser = async (userId: string) => { +export const deleteUserSoft = async (userId: string) => { return true } -export const queryInsertUser = async ( +export const insertUser = async ( username: string, email: string, password: string, @@ -89,6 +89,6 @@ export const queryInsertUser = async ( : null } -export const queryUpdateUser = async (user: User) => { +export const updateUser = async (user: User) => { return user.username === 'testy' } diff --git a/test/routes/book.test.ts b/test/routes/book.test.ts index ba48252..e513d13 100644 --- a/test/routes/book.test.ts +++ b/test/routes/book.test.ts @@ -4,12 +4,13 @@ import { app, pool } from '../../src' jest.mock('../../src/queries/session') jest.mock('../../src/queries/book') +jest.mock('../../src/queries/user') describe('basic endpoint testing for /book', () => { test('get /book/all', async () => { return request(app) .get('/book/all') - .set('Authorization', `Bearer 123`) + .set('Authorization', 'Bearer 123') .expect(200) .expect('Content-Type', /json/) }) @@ -23,20 +24,28 @@ describe('basic endpoint testing for /book', () => { }) test('delete /book', async () => { - return ( - request(app) - .delete('/book?id=1') - .set('Authorization', `Bearer 123`) - //.expect(200) - //.expect("Content-Type", /json/); - .then(() => { + return request(app) + .delete('/book?id=1') + .set('Authorization', `Bearer 123`) + .expect(200) + .expect({ ok: true }) + }) + /** + * .then(() => { expect(200) console.log('book delete test sucessful') }) .catch((error) => { console.error('failed: ', error) }) - ) + */ + + test('get /book/count', async () => { + return request(app) + .get('/book/count') + .set('Authorization', `Bearer 123`) + .expect(200) + .expect('Content-Type', /json/) }) test('post /book', async () => { @@ -53,7 +62,7 @@ describe('basic endpoint testing for /book', () => { }) .set('Authorization', `Bearer 123`) .expect(200) - .expect({ ok: true }) + .expect('Content-Type', /json/) }) test('put /book', async () => { diff --git a/test/routes/book_list.test.ts b/test/routes/book_list.test.ts index 767c777..82ae32c 100644 --- a/test/routes/book_list.test.ts +++ b/test/routes/book_list.test.ts @@ -4,6 +4,7 @@ import { app, pool } from '../../src' jest.mock('../../src/queries/session') jest.mock('../../src/queries/book_list') +jest.mock('../../src/queries/user') describe('basic endpoint testing for /booklist', () => { test('get /booklist/all', async () => { diff --git a/test/routes/book_list_entry.test.ts b/test/routes/book_list_entry.test.ts index ab9ca11..88f888f 100644 --- a/test/routes/book_list_entry.test.ts +++ b/test/routes/book_list_entry.test.ts @@ -4,6 +4,7 @@ import { app, pool } from '../../src' jest.mock('../../src/queries/session') jest.mock('../../src/queries/book_list_entry') +jest.mock('../../src/queries/user') describe('basic endpoint testing for /booklistentry', () => { test('get /booklistentry/all', async () => { diff --git a/test/routes/book_reservation.test.ts b/test/routes/book_reservation.test.ts new file mode 100644 index 0000000..69d1002 --- /dev/null +++ b/test/routes/book_reservation.test.ts @@ -0,0 +1,83 @@ +import { test, describe, jest, afterAll } from '@jest/globals' +import request from 'supertest' +import { app, pool } from '../../src' + +jest.mock('../../src/queries/session') +jest.mock('../../src/queries/book_reservation') +jest.mock('../../src/queries/user') + +describe('basic endpoint testing for /bookreservation', () => { + test('get /bookreservation/all', async () => { + return request(app) + .get('/bookreservation/all') + .set('Authorization', `Bearer 123`) + .expect(200) + .expect('Content-Type', /json/) + }) + + test('get /bookreservation/all/current', async () => { + return request(app) + .get('/bookreservation/all/current') + .set('Authorization', `Bearer 123`) + .expect(200) + .expect('Content-Type', /json/) + }) + + test('get /bookreservation/all/extended', async () => { + return request(app) + .get('/bookreservation/all/extended') + .set('Authorization', `Bearer 123`) + .expect(200) + .expect('Content-Type', /json/) + }) + + test('get /bookreservation/user/current', async () => { + return request(app) + .post('/bookreservation/user/current') + .send({ userId: 3 }) + .set('Authorization', `Bearer 123`) + .expect(200) + .expect('Content-Type', /json/) + }) + + test('get /bookreservation/book', async () => { + return request(app) + .get('/bookreservation/book') + .send({ bookId: 1 }) + .set('Authorization', `Bearer 123`) + .expect(200) + .expect('Content-Type', /json/) + }) + + test('add new reservation /bookreservation/', async () => { + return request(app) + .post('/bookreservation/') + .send({ userId: 4, bookId: 5 }) + .set('Authorization', `Bearer 123`) + .expect(200) + .expect({ ok: true }) + }) + + test('cancel reservation /bookreservation/cancel', async () => { + return request(app) + .post('/bookreservation/cancel') + .send({ bookId: 1 }) + .set('Authorization', `Bearer 123`) + .expect(200) + .expect({ ok: true }) + }) + + test('loan reservation /bookreservation/loan', async () => { + return request(app) + .post('/bookreservation/loan') + .send({ bookId: 3 }) + .set('Authorization', `Bearer 123`) + .expect(200) + .expect({ ok: true }) + }) +}) + +afterAll((done) => { + pool.end() + done() +}) diff --git a/test/routes/borrow.test.ts b/test/routes/borrow.test.ts index fef2434..04bd54d 100644 --- a/test/routes/borrow.test.ts +++ b/test/routes/borrow.test.ts @@ -5,6 +5,7 @@ import { app, pool } from '../../src' jest.mock('../../src/queries/session') jest.mock('../../src/queries/borrow') jest.mock('../../src/queries/book') +jest.mock('../../src/queries/user') describe('basic endpoint testing for /borrow', () => { test('get /borrow/all', async () => { From 3731de2c754a101d4351aef0bedf6473accda8a5 Mon Sep 17 00:00:00 2001 From: Juho9 Date: Thu, 16 Nov 2023 20:12:52 +0200 Subject: [PATCH 02/26] favorite_book jest --- src/queries/__mocks__/book_favorite.ts | 99 ++++++++++++++++++++++++++ test/routes/book_favorite.test.ts | 50 +++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 src/queries/__mocks__/book_favorite.ts create mode 100644 test/routes/book_favorite.test.ts diff --git a/src/queries/__mocks__/book_favorite.ts b/src/queries/__mocks__/book_favorite.ts new file mode 100644 index 0000000..60f1d56 --- /dev/null +++ b/src/queries/__mocks__/book_favorite.ts @@ -0,0 +1,99 @@ +import Book_favorite from '../../interfaces/book_favorite.interface' + +// note: this mock is very basic and its purpose is for jest to run through routes + +let date = new Date() + +let favoritebook1: Book_favorite = { + id: 1, + userId: 1, + bookId: 1, + favoritedDatetime: date, +} + +let favoritebook2: Book_favorite = { + id: 2, + userId: 1, + bookId: 2, + favoritedDatetime: date, +} + +let favoritebook3: Book_favorite = { + id: 3, + userId: 2, + bookId: 1, + favoritedDatetime: date, +} + +let mockFavoriteBookData = [favoritebook1, favoritebook2, favoritebook3] +let sessionId = 1 + +const getBook = (userId: number, bookId: number) => { + for (let index = 0; index < mockFavoriteBookData.length; index++) { + if ( + mockFavoriteBookData[index].userId === userId && + mockFavoriteBookData[index].bookId === bookId + ) { + return mockFavoriteBookData[index] + } + } + return null +} + +export const isBookFavoritedByUser = async ( + userId: number, + bookId: number +): Promise => { + let book = getBook(userId, bookId) + if (book !== null) { + return true + } + return false +} + +export const getFavoriteCountForBook = async ( + bookId: number +): Promise => { + let favoriteCount: number = 0 + for (let index = 0; index < mockFavoriteBookData.length; index++) { + if (mockFavoriteBookData[index].bookId === bookId) { + favoriteCount = favoriteCount + 1 + } + } + return favoriteCount +} + +export const addFavoriteBook = async ( + userId: number, + bookId: number +): Promise => { + let lengthBefore = mockFavoriteBookData.length + mockFavoriteBookData.push({ + id: mockFavoriteBookData.length + 1, + userId: userId, + bookId: bookId, + favoritedDatetime: new Date(), + }) + if (mockFavoriteBookData.length > lengthBefore) { + return true + } + return false +} + +export const deleteFavoriteBook = async ( + userId: number, + bookId: number +): Promise => { + let lengthBefore = mockFavoriteBookData.length + let array: Array = [] + mockFavoriteBookData.forEach((element) => { + if (element.userId !== userId && element.bookId !== bookId) { + array.push(element) + } + }) + mockFavoriteBookData = array + if (lengthBefore > array.length) { + return true + } + return false +} diff --git a/test/routes/book_favorite.test.ts b/test/routes/book_favorite.test.ts new file mode 100644 index 0000000..34be79c --- /dev/null +++ b/test/routes/book_favorite.test.ts @@ -0,0 +1,50 @@ +import { test, describe, jest, afterAll } from '@jest/globals' +import request from 'supertest' +import { app, pool } from '../../src' + +jest.mock('../../src/queries/session') +jest.mock('../../src/queries/book_favorite') +jest.mock('../../src/queries/user') + +describe('basic endpoint testing for /favorite', () => { + test('get /favorite/check', async () => { + return request(app) + .get('/favorite/check') + .query({ bookId: 1 }) + .set('Authorization', `Bearer 123`) + .expect(200) + .expect({ isFavorited: true }) + }) + + test('get /favorite/count', async () => { + return request(app) + .get('/favorite/count') + .query({ bookId: 1 }) + .set('Authorization', `Bearer 123`) + .expect(200) + .expect({ count: 2 }) + }) + + test('add favorite book to user /favorite', async () => { + return request(app) + .post('/favorite/') + .send({ bookId: 3 }) + .set('Authorization', `Bearer 123`) + .expect(200) + .expect({ ok: true }) + }) + + test('delete favorite book from user /favorite', async () => { + return request(app) + .delete('/favorite/') + .send({ bookId: 2 }) + .set('Authorization', `Bearer 123`) + .expect(200) + .expect({ ok: true }) + }) +}) + +afterAll((done) => { + pool.end() + done() +}) From 269dcdb1710ad6ac854b5ebfde3ddd2f35f30039 Mon Sep 17 00:00:00 2001 From: Juho9 Date: Thu, 16 Nov 2023 20:21:05 +0200 Subject: [PATCH 03/26] book_review jest --- src/queries/__mocks__/book_review.ts | 134 +++++++++++++++++++++++++++ test/routes/book_review.test.ts | 76 +++++++++++++++ 2 files changed, 210 insertions(+) create mode 100644 src/queries/__mocks__/book_review.ts create mode 100644 test/routes/book_review.test.ts diff --git a/src/queries/__mocks__/book_review.ts b/src/queries/__mocks__/book_review.ts new file mode 100644 index 0000000..b152f4e --- /dev/null +++ b/src/queries/__mocks__/book_review.ts @@ -0,0 +1,134 @@ +import Book_review from '../../interfaces/book_review.interface' + +const date = new Date() + +const mockBookReviews: Book_review[] = [ + { + id: 1, + userId: 1, + bookId: 1, + comment: 'A great book!', + rating: 5, + reviewDate: date, + }, + { + id: 2, + userId: 2, + bookId: 2, + comment: 'Enjoyable read', + rating: 4, + reviewDate: date, + }, + { + id: 3, + userId: 3, + bookId: 1, + comment: 'Not my favorite', + rating: 3, + reviewDate: date, + }, + { + id: 4, + userId: 1, + bookId: 3, + comment: 'Loved it!', + rating: 5, + reviewDate: date, + }, + { + id: 5, + userId: 2, + bookId: 3, + comment: 'Highly recommended', + rating: 5, + reviewDate: date, + }, +] + +export const getAllReviews = async (): Promise => { + return mockBookReviews as Book_review[] +} + +export const getReviewByBookId = async ( + bookId: number +): Promise => { + let array: Array = [] + for (let index = 0; index < mockBookReviews.length; index++) { + if (mockBookReviews[index].bookId === bookId) { + array.push(mockBookReviews[index]) + } + } + return array.length > 0 ? (array as Book_review[]) : null +} + +export const deleteReview = async (id: number): Promise => { + let array: Array = [] + for (let index = 0; index < mockBookReviews.length; index++) { + if (mockBookReviews[index].id !== id) { + array.push(mockBookReviews[index]) + } + } + if (mockBookReviews.length > array.length) { + return true + } + return false +} + +export const updateReview = async ( + reviewId: number, + comment: string, + rating: number +): Promise => { + mockBookReviews.forEach((element) => { + if (element.id === reviewId) { + element.comment = comment + element.rating = rating + } + }) + return true +} + +export const insertReview = async ( + userId: number, + bookId: number, + comment: string, + rating: number +): Promise => { + mockBookReviews.push({ + id: mockBookReviews.length + 1, + userId: userId, + bookId: bookId, + comment: comment, + rating: rating, + reviewDate: new Date(), + }) + return true +} + +export const getAverageRatingForBook = async ( + bookId: number +): Promise => { + let totalRating: number = 0 + let numberOfReviews: number = 0 + let averageRating: number = 0 + mockBookReviews.forEach((element) => { + if (element.bookId === bookId) { + totalRating = totalRating + element.rating + numberOfReviews++ + } + }) + averageRating = totalRating / numberOfReviews + return averageRating +} + +export const getReviewById = async ( + reviewId: number +): Promise => { + let array: Array = [] + for (let index = 0; index < mockBookReviews.length; index++) { + if (mockBookReviews[index].id === reviewId) { + array.push(mockBookReviews[index]) + } + } + return array.length > 0 ? (array[0] as Book_review) : null +} diff --git a/test/routes/book_review.test.ts b/test/routes/book_review.test.ts new file mode 100644 index 0000000..3f987e8 --- /dev/null +++ b/test/routes/book_review.test.ts @@ -0,0 +1,76 @@ +import { test, describe, jest, afterAll } from '@jest/globals' +import request from 'supertest' +import { app, pool } from '../../src' + +jest.mock('../../src/queries/session') +jest.mock('../../src/queries/book_review') +jest.mock('../../src/queries/user') + +describe('basic endpoint testing for /review', () => { + test('get all bookreviews /review/all', async () => { + return request(app) + .get('/review/all') + .set('Authorization', `Bearer 123`) + .expect(200) + .expect('Content-Type', /json/) + }) + + test('get review by bookId /review/book', async () => { + return request(app) + .get('/review/book') + .query({ bookId: 1 }) + .set('Authorization', `Bearer 123`) + .expect(200) + .expect('Content-Type', /json/) + }) + + test('get review average for book /review/average', async () => { + return request(app) + .get('/review/average') + .query({ bookId: 1 }) + .set('Authorization', `Bearer 123`) + .expect(200) + .expect({ averageRating: 4 }) + }) + + test('delete review /review', async () => { + return request(app) + .delete('/review/') + .send({ reviewId: 1 }) + .set('Authorization', `Bearer 123`) + .expect(200) + .expect({ ok: true }) + }) + + test('delete non-existing review /review', async () => { + return request(app) + .delete('/review/') + .send({ reviewId: 16 }) + .set('Authorization', `Bearer 123`) + .expect(404) + .expect({ ok: false, error: 'Review not found.' }) + }) + + test('add review /review', async () => { + return request(app) + .post('/review/') + .send({ bookId: 1, comment: 'Nice book for testing', rating: 5 }) + .set('Authorization', `Bearer 123`) + .expect(200) + .expect({ ok: true }) + }) + + test('update review /review', async () => { + return request(app) + .put('/review/') + .send({ rewiewId: 5, comment: 'Nevermind', rating: 1 }) + .set('Authorization', `Bearer 123`) + .expect(200) + .expect({ ok: true }) + }) +}) + +afterAll((done) => { + pool.end() + done() +}) From 22614f8196b7c44f66f3e0457e4913706d5b580b Mon Sep 17 00:00:00 2001 From: Juho9 Date: Thu, 16 Nov 2023 20:45:31 +0200 Subject: [PATCH 04/26] book_request jest with updated function names --- src/queries/__mocks__/book_request.ts | 83 +++++++++++++++++++++++++++ test/routes/book_request.test.ts | 44 ++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 src/queries/__mocks__/book_request.ts create mode 100644 test/routes/book_request.test.ts diff --git a/src/queries/__mocks__/book_request.ts b/src/queries/__mocks__/book_request.ts new file mode 100644 index 0000000..2bebc70 --- /dev/null +++ b/src/queries/__mocks__/book_request.ts @@ -0,0 +1,83 @@ +import Book_request from '../../interfaces/book_request.interface' +import { Book_request_status } from '../../interfaces/book_request.interface' + +const mockBookRequests: Book_request[] = [ + { + id: 1, + userId: 1, + isbn: '978-1234567890', + title: 'Sample Book 1', + reason: 'I want to read this book.', + status: 0, + }, + { + id: 2, + userId: 2, + isbn: '978-9876543210', + title: 'Another Book', + reason: 'I need this for my research.', + status: 0, + }, + { + id: 3, + userId: 3, + isbn: '978-5678901234', + title: 'The Third Book', + reason: 'This book is a must-read!', + status: 2, + }, + { + id: 4, + userId: 1, + isbn: '978-3456789012', + title: 'Request Book 4', + reason: 'I heard its a great book.', + status: 0, + }, + { + id: 5, + userId: 2, + isbn: '978-2109876543', + title: 'Book Five', + reason: 'Please approve my request.', + status: 1, + }, +] + +export const getAllRequests = async (): Promise => { + return mockBookRequests +} + +export const insertRequest = async ( + userId: number, + isbn: string, + title: string, + reason: string +): Promise => { + let lengthBefore = mockBookRequests.length + mockBookRequests.push({ + id: mockBookRequests.length + 1, + userId: userId, + isbn: isbn, + title: title, + reason: reason, + status: 0, + }) + if (mockBookRequests.length > lengthBefore) { + return true + } + return false +} + +export const updateRequestStatus = async ( + id: number, + status: Book_request_status +): Promise => { + let index = mockBookRequests.findIndex((element) => element.id === id) + if (index < 0 && index > mockBookRequests.length) { + return false + } else { + mockBookRequests[index].status = status + return true + } +} diff --git a/test/routes/book_request.test.ts b/test/routes/book_request.test.ts new file mode 100644 index 0000000..8f6f67c --- /dev/null +++ b/test/routes/book_request.test.ts @@ -0,0 +1,44 @@ +import { test, describe, jest, afterAll } from '@jest/globals' +import request from 'supertest' +import { app, pool } from '../../src' + +jest.mock('../../src/queries/session') +jest.mock('../../src/queries/book_request') +jest.mock('../../src/queries/user') + +describe('basic endpoint testing for /bookrequest', () => { + test('get all requests /bookrequest/all', async () => { + return request(app) + .get('/bookrequest/all') + .set('Authorization', `Bearer 123`) + .expect(200) + .expect('Content-Type', /json/) + }) + + test('add new request for book /bookrequest', async () => { + return request(app) + .post('/bookrequest/') + .send({ + isbn: '9789206334843', + title: 'Cars', + reason: 'It helps me focus', + }) + .set('Authorization', `Bearer 123`) + .expect(200) + .expect({ ok: true }) + }) + + test('update request status /bookrequest/updatestatus', async () => { + return request(app) + .put('/bookrequest/updatestatus') + .send({ id: 2, status: 2 }) + .set('Authorization', `Bearer 123`) + .expect(200) + .expect({ ok: true }) + }) +}) + +afterAll((done) => { + pool.end() + done() +}) From 996ec267e413dbe1d4a09415955bb5d93203f148 Mon Sep 17 00:00:00 2001 From: Juho9 Date: Thu, 16 Nov 2023 21:18:22 +0200 Subject: [PATCH 05/26] office jest --- src/queries/__mocks__/office.ts | 94 +++++++++++++++++++++++++++++++++ test/routes/office.test.ts | 56 ++++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 src/queries/__mocks__/office.ts create mode 100644 test/routes/office.test.ts diff --git a/src/queries/__mocks__/office.ts b/src/queries/__mocks__/office.ts new file mode 100644 index 0000000..a612a2c --- /dev/null +++ b/src/queries/__mocks__/office.ts @@ -0,0 +1,94 @@ +import { HomeOffice } from '../../interfaces/HomeOffice' + +let mockHomeOffices: HomeOffice[] = [ + { + id: 1, + name: 'Home Office 1', + countryCode: 'USA', + }, + { + id: 2, + name: 'Home Office 2', + countryCode: 'CAN', + }, + { + id: 3, + name: 'Home Office 3', + countryCode: 'GBR', + }, + { + id: 4, + name: 'Home Office 4', + countryCode: 'FRA', + }, + { + id: 5, + name: 'Home Office 5', + countryCode: 'DEU', + }, +] + +let idCount: number = 5 + +export async function getHomeOfficeById( + homeOfficeId: number +): Promise { + let array: Array = [] + mockHomeOffices.forEach((element) => { + if (element.id === homeOfficeId) { + array.push(element) + } + }) + return array.length ? array[0] : null +} + +export async function getAllHomeOffices(): Promise { + return !mockHomeOffices.length ? [] : mockHomeOffices.map((value) => value) +} + +export async function deleteHomeOffice(homeOfficeId: number): Promise { + mockHomeOffices = mockHomeOffices.filter( + (element) => element.id !== homeOfficeId + ) + let index = mockHomeOffices.findIndex( + (element) => element.id === homeOfficeId + ) + for (let i = 0; i < mockHomeOffices.length; i++) { + if (mockHomeOffices[i].id === index) { + return false + } + } + return true +} + +export async function updateHomeOffice( + homeOffice: HomeOffice +): Promise { + for (let index = 0; index < mockHomeOffices.length; index++) { + if (mockHomeOffices[index].id === homeOffice.id) { + mockHomeOffices[index] = { + id: mockHomeOffices[index].id, + name: homeOffice.name, + countryCode: homeOffice.countryCode, + } + return true + } + } + return false +} + +export async function insertHomeOffice( + name: string, + countryCode: string +): Promise { + let length = mockHomeOffices.length + mockHomeOffices.push({ + id: idCount + 1, + name: name, + countryCode: countryCode, + }) + if (mockHomeOffices.length > length) { + return true + } + return false +} diff --git a/test/routes/office.test.ts b/test/routes/office.test.ts new file mode 100644 index 0000000..217495e --- /dev/null +++ b/test/routes/office.test.ts @@ -0,0 +1,56 @@ +import { test, describe, jest, afterAll } from '@jest/globals' +import request from 'supertest' +import { app, pool } from '../../src' + +jest.mock('../../src/queries/session') +jest.mock('../../src/queries/office') +jest.mock('../../src/queries/user') + +describe('basic endpoint testing for /office', () => { + test('get all offices /office/all', async () => { + return request(app) + .get('/office/all') + .set('Authorization', `Bearer 123`) + .expect(200) + .expect('Content-Type', /json/) + }) + + test('get office based on id /office/:homeOfficeId', async () => { + return request(app) + .get('/office/1') + .set('Authorization', `Bearer 123`) + .expect(200) + .expect({ id: 1, name: 'Home Office 1', countryCode: 'USA' }) + }) + + test('delete office /office/:homeOfficeId', async () => { + return request(app) + .delete('/office/2') + .set('Authorization', `Bearer 123`) + .expect(200) + .expect('true') + }) + + test('update office /office/:homeOfficeId', async () => { + return request(app) + .put('/office/1') + .send({ name: 'Uzbekistan', countryCode: 'UZB' }) + .set('Authorization', `Bearer 123`) + .expect(200) + .expect({ ok: true }) + }) + + test('insert office /office', async () => { + return request(app) + .post('/office/') + .send({ name: 'Tadzikistan', countryCode: 'TZK' }) + .set('Authorization', `Bearer 123`) + .expect(200) + .expect({ ok: true }) + }) +}) + +afterAll((done) => { + pool.end() + done() +}) From 828fd8a1725a4f3678c65e9eab38ac0aad5250a4 Mon Sep 17 00:00:00 2001 From: Juho9 Date: Tue, 21 Nov 2023 16:05:58 +0200 Subject: [PATCH 06/26] borrow robot tests --- robot/tests/05_borrowTests.robot | 50 ++++++++++++++++++++++++++++++++ robot/tests/12_logoutTests.robot | 10 +++++++ 2 files changed, 60 insertions(+) create mode 100644 robot/tests/05_borrowTests.robot create mode 100644 robot/tests/12_logoutTests.robot diff --git a/robot/tests/05_borrowTests.robot b/robot/tests/05_borrowTests.robot new file mode 100644 index 0000000..fd20bf5 --- /dev/null +++ b/robot/tests/05_borrowTests.robot @@ -0,0 +1,50 @@ +*** Settings *** +Resource ../common.resource +Library RequestsLibrary +Library String + +*** Test Cases *** +Verify that all borrows can be found + ${response}= GET url=${URL}/borrow/all?${bearerToken} expected_status=200 + +Verify that user can find borrow by id + ${response}= GET url=${URL}/borrow/?id=1&${bearerToken} expected_status=200 + Should Be Equal ${response.json()['id']} ${1} + +Verify that user can't find borrow by non-existing id + ${response}= GET url=${URL}/borrow/?id=12345&${bearerToken} expected_status=200 + Should Be Equal ${response.json()} ${none} + +Verify that user can check borrowed book by id + ${response}= GET url=${URL}/borrow/?id=1&${bearerToken} expected_status=200 + Should Be Equal ${response.json()['id']} ${1} + +Verify that new borrow for book can be created + &{data}= Create dictionary + ... { bookId=2 } + ${response}= POST url=${URL}/borrow?${bearerToken} json=${data} expected_status=200 + Should Be True ${response.json()['ok']} + +Verify that user can't delete non-existing borrow + &{data}= Create dictionary + ... { bookId=1234 } + ${response}= DELETE url=${URL}/borrow?${bearerToken} json=${data} expected_status=200 + Should Not Be True ${response.json()['ok']} + +Verify that user can get current borrows + ${response}= GET url=${URL}/borrow/current?${bearerToken} expected_status=200 + +Verify that user can get borrows by session + ${response}= GET url=${URL}/borrow/session${bearerToken} expected_status=200 + +Verify that borrow can be returned + &{data}= Create dictionary + ... { bookId=2 } + ${response}= PUT url=${URL}/borrow/return?${bearerToken} json=${data} expected_status=200 + Should Be True ${response.json()['ok']} + +Verify that user can delete borrow + &{data}= Create dictionary + ... { bookId=2 } + ${response}= DELETE url=${URL}/borrow?${bearerToken} json=${data} expected_status=200 + Should Be True ${response.json()['ok']} \ No newline at end of file diff --git a/robot/tests/12_logoutTests.robot b/robot/tests/12_logoutTests.robot new file mode 100644 index 0000000..26a4a1e --- /dev/null +++ b/robot/tests/12_logoutTests.robot @@ -0,0 +1,10 @@ +*** Settings *** +Resource ../common.resource +Library RequestsLibrary +Library String + + +*** Test Cases *** +Verify user can logout + ${response}= POST ${URL}/auth/logout?${bearerToken} expected_status=200 + Should Be True ${response.json()['ok']} From 79b3c4a79319d07ef95ea5e363b52f8e3d502be7 Mon Sep 17 00:00:00 2001 From: Juho9 Date: Tue, 21 Nov 2023 16:21:52 +0200 Subject: [PATCH 07/26] modified typo and wrong status at borrow_test --- robot/tests/05_borrowTests.robot | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/robot/tests/05_borrowTests.robot b/robot/tests/05_borrowTests.robot index fd20bf5..3051d1d 100644 --- a/robot/tests/05_borrowTests.robot +++ b/robot/tests/05_borrowTests.robot @@ -21,30 +21,23 @@ Verify that user can check borrowed book by id Verify that new borrow for book can be created &{data}= Create dictionary - ... { bookId=2 } + ... { bookId=1 } ${response}= POST url=${URL}/borrow?${bearerToken} json=${data} expected_status=200 Should Be True ${response.json()['ok']} Verify that user can't delete non-existing borrow &{data}= Create dictionary ... { bookId=1234 } - ${response}= DELETE url=${URL}/borrow?${bearerToken} json=${data} expected_status=200 - Should Not Be True ${response.json()['ok']} + ${response}= DELETE url=${URL}/borrow?${bearerToken} json=${data} expected_status=403 Verify that user can get current borrows ${response}= GET url=${URL}/borrow/current?${bearerToken} expected_status=200 Verify that user can get borrows by session - ${response}= GET url=${URL}/borrow/session${bearerToken} expected_status=200 + ${response}= GET url=${URL}/borrow/session?${bearerToken} expected_status=200 Verify that borrow can be returned &{data}= Create dictionary - ... { bookId=2 } + ... { bookId=1 } ${response}= PUT url=${URL}/borrow/return?${bearerToken} json=${data} expected_status=200 Should Be True ${response.json()['ok']} - -Verify that user can delete borrow - &{data}= Create dictionary - ... { bookId=2 } - ${response}= DELETE url=${URL}/borrow?${bearerToken} json=${data} expected_status=200 - Should Be True ${response.json()['ok']} \ No newline at end of file From 0187c37d784743af09cb8df6e7d9dc42747d2675 Mon Sep 17 00:00:00 2001 From: Juho9 Date: Tue, 21 Nov 2023 19:47:39 +0200 Subject: [PATCH 08/26] removed admin tests from borrow --- robot/tests/05_borrowTests.robot | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/robot/tests/05_borrowTests.robot b/robot/tests/05_borrowTests.robot index 3051d1d..47c8703 100644 --- a/robot/tests/05_borrowTests.robot +++ b/robot/tests/05_borrowTests.robot @@ -19,12 +19,6 @@ Verify that user can check borrowed book by id ${response}= GET url=${URL}/borrow/?id=1&${bearerToken} expected_status=200 Should Be Equal ${response.json()['id']} ${1} -Verify that new borrow for book can be created - &{data}= Create dictionary - ... { bookId=1 } - ${response}= POST url=${URL}/borrow?${bearerToken} json=${data} expected_status=200 - Should Be True ${response.json()['ok']} - Verify that user can't delete non-existing borrow &{data}= Create dictionary ... { bookId=1234 } @@ -36,8 +30,3 @@ Verify that user can get current borrows Verify that user can get borrows by session ${response}= GET url=${URL}/borrow/session?${bearerToken} expected_status=200 -Verify that borrow can be returned - &{data}= Create dictionary - ... { bookId=1 } - ${response}= PUT url=${URL}/borrow/return?${bearerToken} json=${data} expected_status=200 - Should Be True ${response.json()['ok']} From d10b4eb2f09e6d46a1453f23a15baa70e2e11c8f Mon Sep 17 00:00:00 2001 From: Juho9 Date: Tue, 21 Nov 2023 21:44:31 +0200 Subject: [PATCH 09/26] robottests for booklist and favorites --- robot/tests/04_bookTests.robot | 19 ++---- robot/tests/05_logoutTests.robot | 10 ---- robot/tests/06_booklistTests.robot | 83 ++++++++++++++++++++++++++ robot/tests/07_bookFavoriteTests.robot | 26 ++++++++ 4 files changed, 113 insertions(+), 25 deletions(-) delete mode 100644 robot/tests/05_logoutTests.robot create mode 100644 robot/tests/06_booklistTests.robot create mode 100644 robot/tests/07_bookFavoriteTests.robot diff --git a/robot/tests/04_bookTests.robot b/robot/tests/04_bookTests.robot index 07128ad..d42133c 100644 --- a/robot/tests/04_bookTests.robot +++ b/robot/tests/04_bookTests.robot @@ -36,21 +36,10 @@ Verify that a nonexistent book can't be deleted ${response}= DELETE url=${URL}/book/?id=12321&${bearerToken} expected_status=403 Should Not Be True ${response.json()['ok']} -Verify user can check all borrowed books - ${response}= GET url=${URL}/borrow/all?${bearerToken} expected_status=200 - -Verify that user cannot get a nonexisting borrowed book - ${response}= GET url=${URL}/borrow/?id=123456789&${bearerToken} expected_status=200 - Should Be Equal ${response.json()} ${None} - -Verify that user can check borrowed book by id - ${response}= GET url=${URL}/borrow/?id=1&${bearerToken} expected_status=200 - Should Be Equal ${response.json()['id']} ${1} - -# Verify that book can be updated -# &{data}= Create dictionary { library_user=2 title=Fake asdf image=http://books.google.com/books/content?id=ILqrxQEACAAJ&printsec=frontcover&img=1&zoom=1&source=gbs_api author=Test Author year=2022 isbn=123-242-421 topic=js location=Fake location deleted=0} -# ${response}= PUT url=${URL}/book/?id=12&${bearerToken} json=${data} expected_status=200 -# Should Be True ${response.json()['ok']} +Verify that book can be updated + &{data}= Create dictionary { library_user=2 title=Fake asdf image=http://books.google.com/books/content?id=ILqrxQEACAAJ&printsec=frontcover&img=1&zoom=1&source=gbs_api author=Test Author year=2022 isbn=123-242-421 topic=js location=Fake location deleted=0} + ${response}= PUT url=${URL}/book/?id=4&${bearerToken} json=${data} expected_status=200 + Should Be True ${response.json()['ok']} # Verify that a book can be deleted # ${response}= DELETE url=${URL}/book/?id=10&${bearerToken} expected_status=200 diff --git a/robot/tests/05_logoutTests.robot b/robot/tests/05_logoutTests.robot deleted file mode 100644 index 26a4a1e..0000000 --- a/robot/tests/05_logoutTests.robot +++ /dev/null @@ -1,10 +0,0 @@ -*** Settings *** -Resource ../common.resource -Library RequestsLibrary -Library String - - -*** Test Cases *** -Verify user can logout - ${response}= POST ${URL}/auth/logout?${bearerToken} expected_status=200 - Should Be True ${response.json()['ok']} diff --git a/robot/tests/06_booklistTests.robot b/robot/tests/06_booklistTests.robot new file mode 100644 index 0000000..65cd4e6 --- /dev/null +++ b/robot/tests/06_booklistTests.robot @@ -0,0 +1,83 @@ +*** Settings *** +Resource ../common.resource +Library RequestsLibrary +Library String + +*** Variables *** +${okTrueJson} {"ok": true} +${okFalseJson} {"ok": false} + + +*** Test Cases *** +Verify that user can add booklists + ${data}= Create dictionary + ... { name='Testlist' } + ${response}= POST url=${URL}/booklist?${bearerToken} json=${data} expected_status=200 + +Verify that user can check all booklists + ${response}= GET url=${URL}/booklist/all?${bearerToken} expected_status=200 + +Verify that user can check all users booklists + ${response}= GET url=${URL}/booklist/user?${bearerToken} expected_status=200 + ${bookList}= Evaluate json.loads('''${response.text}''') + Length Should Be ${bookList} 1 + +Verify that user can add books to list + ${data}= Create dictionary + ... list=1 + ... book=2 + ${response}= POST url=${URL}/booklistentry?${bearerToken} json=${data} expected_status=200 + Should Be Equal As Strings ${response.text} ${okTrueJson} + +Verify that user can't add non-existing books to list + ${data}= Create dictionary + ... list=1 + ... book=2123 + ${response}= POST url=${URL}/booklistentry?${bearerToken} json=${data} expected_status=500 + Should Be Equal As Strings ${response.text} ${okFalseJson} + +Verify that user can't add books to non-existing list + ${data}= Create dictionary + ... list=11234 + ... book=2 + ${response}= POST url=${URL}/booklistentry?${bearerToken} json=${data} expected_status=500 + Should Be Equal As Strings ${response.text} ${okFalseJson} + +Verify that user can get all booklistentrys + ${response}= GET url=${URL}/booklistentry/all?${bearerToken} expected_status=200 + +Verify that user get all booklistentrys based on listId + ${response}= GET url=${URL}/booklistentry/list?id=1${bearerToken} expected_status=200 + ${bookList}= Evaluate json.loads('''${response.text}''') + Length Should Be ${bookList} 1 + +Verify that user get all books in list based on listId + ${response}= GET url=${URL}/booklist/books?id=1${bearerToken} expected_status=200 + ${bookList}= Evaluate json.loads('''${response.text}''') + Length Should Be ${bookList} 1 + +Verify that user can modify booklist + ${data}= Create dictionary + ... id=1 + ... name='New name' + ${response}= PUT url=${URL}/booklist?${bearerToken} json=${data} expected_status=200 + Should Be Equal As Strings ${response.text} ${okTrueJson} + +Verify that user can't modify non-existing booklist + ${data}= Create dictionary + ... id=12345 + ... name='Wrong list' + ${response}= PUT url=${URL}/booklist?${bearerToken} json=${data} expected_status=200 + Should Be Equal As Strings ${response.text} ${okFalseJson} + +Verify that user can delete booklist + ${data}= Create dictionary + ... id=1 + ${response}= DELETE url=${URL}/booklist?${bearerToken} json=${data} expected_status=200 + Should Be Equal As Strings ${response.text} ${okTrueJson} + +Verify that user can't delete non-existing booklist + ${data}= Create dictionary + ... id=1123 + ${response}= DELETE url=${URL}/booklist?${bearerToken} json=${data} expected_status=200 + Should Be Equal As Strings ${response.text} ${okFalseJson} \ No newline at end of file diff --git a/robot/tests/07_bookFavoriteTests.robot b/robot/tests/07_bookFavoriteTests.robot new file mode 100644 index 0000000..3be65bf --- /dev/null +++ b/robot/tests/07_bookFavoriteTests.robot @@ -0,0 +1,26 @@ +*** Settings *** +Resource ../common.resource +Library RequestsLibrary +Library String + +*** Test Cases *** +Verify that user can add favorite + &{data}= Create dictionary + ... { bookId=3 } + ${response}= POST url=${URL}/favorite?${bearerToken} expected_status=200 + Should Be True ${response.json()['ok']} + +Verify that user can get counts for all favorited books + ${response}= GET url=${URL}/favorite/counts?${bearerToken} expected_status=200 + +Verify that user can delete book from favorites + &{data}= Create dictionary + ... { bookId=3 } + ${response}= DELETE url=${URL}/favorite?${bearerToken} expected_status=200 + Should Be True ${response.json()['ok']} + +Verify that user can't delete book that isn't favorited + &{data}= Create dictionary + ... { bookId=3123 } + ${response}= DELETE url=${URL}/favorite?${bearerToken} expected_status=400 + Should Not Be True ${response.json()['ok']} From 18a1fab0b027214c0121caa350b683439f2cfcb1 Mon Sep 17 00:00:00 2001 From: Juho9 Date: Wed, 22 Nov 2023 00:23:20 +0200 Subject: [PATCH 10/26] added tests for bookreviews and fixed typo on booklists --- robot/tests/04_bookTests.robot | 9 +++-- robot/tests/06_booklistTests.robot | 13 ++++--- robot/tests/07_bookFavoriteTests.robot | 4 +- robot/tests/08_bookReviewTests.robot | 51 ++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 11 deletions(-) create mode 100644 robot/tests/08_bookReviewTests.robot diff --git a/robot/tests/04_bookTests.robot b/robot/tests/04_bookTests.robot index d42133c..dbfe3da 100644 --- a/robot/tests/04_bookTests.robot +++ b/robot/tests/04_bookTests.robot @@ -36,10 +36,11 @@ Verify that a nonexistent book can't be deleted ${response}= DELETE url=${URL}/book/?id=12321&${bearerToken} expected_status=403 Should Not Be True ${response.json()['ok']} -Verify that book can be updated - &{data}= Create dictionary { library_user=2 title=Fake asdf image=http://books.google.com/books/content?id=ILqrxQEACAAJ&printsec=frontcover&img=1&zoom=1&source=gbs_api author=Test Author year=2022 isbn=123-242-421 topic=js location=Fake location deleted=0} - ${response}= PUT url=${URL}/book/?id=4&${bearerToken} json=${data} expected_status=200 - Should Be True ${response.json()['ok']} + +# Verify that book can be updated +# &{data}= Create dictionary { library_user=2 title=Fake asdf image=http://books.google.com/books/content?id=ILqrxQEACAAJ&printsec=frontcover&img=1&zoom=1&source=gbs_api author=Test Author year=2022 isbn=123-242-421 topic=js location=Fake location deleted=0} +# ${response}= PUT url=${URL}/book/?id=12&${bearerToken} json=${data} expected_status=200 +# Should Be True ${response.json()['ok']} # Verify that a book can be deleted # ${response}= DELETE url=${URL}/book/?id=10&${bearerToken} expected_status=200 diff --git a/robot/tests/06_booklistTests.robot b/robot/tests/06_booklistTests.robot index 65cd4e6..e442537 100644 --- a/robot/tests/06_booklistTests.robot +++ b/robot/tests/06_booklistTests.robot @@ -4,8 +4,9 @@ Library RequestsLibrary Library String *** Variables *** -${okTrueJson} {"ok": true} -${okFalseJson} {"ok": false} +${okTrueJson} {"ok":true} +${okFalseJson} {"ok":false} +${listId} {EMPTY} *** Test Cases *** @@ -16,7 +17,9 @@ Verify that user can add booklists Verify that user can check all booklists ${response}= GET url=${URL}/booklist/all?${bearerToken} expected_status=200 - + ${json_response}= Evaluate json.loads('''${response.text}''') json + ${listId}= Set Variable ${json_response[-1]['id']} + Verify that user can check all users booklists ${response}= GET url=${URL}/booklist/user?${bearerToken} expected_status=200 ${bookList}= Evaluate json.loads('''${response.text}''') @@ -47,12 +50,12 @@ Verify that user can get all booklistentrys ${response}= GET url=${URL}/booklistentry/all?${bearerToken} expected_status=200 Verify that user get all booklistentrys based on listId - ${response}= GET url=${URL}/booklistentry/list?id=1${bearerToken} expected_status=200 + ${response}= GET url=${URL}/booklistentry/list?id=${listId}${bearerToken} expected_status=200 ${bookList}= Evaluate json.loads('''${response.text}''') Length Should Be ${bookList} 1 Verify that user get all books in list based on listId - ${response}= GET url=${URL}/booklist/books?id=1${bearerToken} expected_status=200 + ${response}= GET url=${URL}/booklist/books?id=${listId}${bearerToken} expected_status=200 ${bookList}= Evaluate json.loads('''${response.text}''') Length Should Be ${bookList} 1 diff --git a/robot/tests/07_bookFavoriteTests.robot b/robot/tests/07_bookFavoriteTests.robot index 3be65bf..49c5aa7 100644 --- a/robot/tests/07_bookFavoriteTests.robot +++ b/robot/tests/07_bookFavoriteTests.robot @@ -6,7 +6,7 @@ Library String *** Test Cases *** Verify that user can add favorite &{data}= Create dictionary - ... { bookId=3 } + ... { bookId=1 } ${response}= POST url=${URL}/favorite?${bearerToken} expected_status=200 Should Be True ${response.json()['ok']} @@ -15,7 +15,7 @@ Verify that user can get counts for all favorited books Verify that user can delete book from favorites &{data}= Create dictionary - ... { bookId=3 } + ... { bookId=1 } ${response}= DELETE url=${URL}/favorite?${bearerToken} expected_status=200 Should Be True ${response.json()['ok']} diff --git a/robot/tests/08_bookReviewTests.robot b/robot/tests/08_bookReviewTests.robot new file mode 100644 index 0000000..8ad7891 --- /dev/null +++ b/robot/tests/08_bookReviewTests.robot @@ -0,0 +1,51 @@ +*** Settings *** +Resource ../common.resource +Library RequestsLibrary +Library String + +*** Variables *** +${okTrueJson} {"ok":true} +${okFalseJson} {"ok":false} +${reviewId} {EMPTY} #For modified value + + +*** Test Cases *** +Verify that user can add review to book + ${data}= Create dictionary + ... bookId=1 + ... comment='Great book for everyone!' + ... rating=5 + ${response}= POST url=${URL}/review?${bearerToken} json=${data} expected_status=200 + Should Be Equal As Strings ${response.text} ${okTrueJson} + +Verify that user can't add review to non-existing book + ${data}= Create dictionary + ... bookId=1124 + ... comment='Wrong book' + ... rating=1 + ${response}= POST url=${URL}/review?${bearerToken} json=${data} expected_status=500 + Should Be Equal As Strings ${response.text} ${okFalseJson} + +Verify that user can check all reviews + ${response}= GET url=${URL}/review/all?${bearerToken} expected_status=200 + ${json_response}= Evaluate json.loads('''${response.text}''') json + ${reviewId}= Set Variable ${json_response[-1]['reviewId']} + +Verify that user can check reviews for all books + ${response}= GET url=${URL}/review/reviews?${bearerToken} expected_status=200 + +Verify that user can modify review + ${data}= Create dictionary + ... comment='Modified book' + ... rating=1 + ... reviewId=${reviewId} + ${response}= POST url=${URL}/review?${bearerToken} json=${data} expected_status=200 + Should Be Equal As Strings ${response.text} ${okTrueJson} + +Verify that user can't modify non-existing review + ${data}= Create dictionary + ... comment='Again wrong book' + ... rating=1 + ... reviewId=1234 + ${response}= POST url=${URL}/review?${bearerToken} json=${data} expected_status=400 + Should Be Equal As Strings ${response.text} ${okFalseJson} \ No newline at end of file From 8eac67dca38bd698fffe44d869c966e75e9f0554 Mon Sep 17 00:00:00 2001 From: Juho9 Date: Wed, 22 Nov 2023 02:08:24 +0200 Subject: [PATCH 11/26] tests for request and reservation --- robot/common.resource | 2 + robot/tests/01_loginTests.robot | 2 + robot/tests/06_booklistTests.robot | 10 +-- robot/tests/07_bookFavoriteTests.robot | 16 +++- robot/tests/08_bookReviewTests.robot | 5 +- robot/tests/09_bookRequestTests.robot | 18 +++++ robot/tests/10_bookReservationTests.robot | 75 +++++++++++++++++++ ...logoutTests.robot => 11_logoutTests.robot} | 0 8 files changed, 113 insertions(+), 15 deletions(-) create mode 100644 robot/tests/09_bookRequestTests.robot create mode 100644 robot/tests/10_bookReservationTests.robot rename robot/tests/{12_logoutTests.robot => 11_logoutTests.robot} (100%) diff --git a/robot/common.resource b/robot/common.resource index 0cda399..1da7704 100644 --- a/robot/common.resource +++ b/robot/common.resource @@ -8,3 +8,5 @@ ${BACKENDTESTPASSWORD} default # this will be set in initial login test ${bearerToken} default +${userId} default + diff --git a/robot/tests/01_loginTests.robot b/robot/tests/01_loginTests.robot index 1c62f80..3da598b 100644 --- a/robot/tests/01_loginTests.robot +++ b/robot/tests/01_loginTests.robot @@ -33,4 +33,6 @@ Verify user can login ${response}= POST ${URL}/auth/login json=${data} expected_status=200 Should Be True ${response.json()['ok']} Set Global Variable ${bearerToken} access_token=${response.json()['secret']} + Set Global Variable ${userId} ${response.json()['userId']} Log To Console ${bearerToken} + Log To Console ${userId} diff --git a/robot/tests/06_booklistTests.robot b/robot/tests/06_booklistTests.robot index e442537..eb70f15 100644 --- a/robot/tests/06_booklistTests.robot +++ b/robot/tests/06_booklistTests.robot @@ -6,19 +6,15 @@ Library String *** Variables *** ${okTrueJson} {"ok":true} ${okFalseJson} {"ok":false} -${listId} {EMPTY} - *** Test Cases *** Verify that user can add booklists ${data}= Create dictionary - ... { name='Testlist' } + ... name='Testlist' ${response}= POST url=${URL}/booklist?${bearerToken} json=${data} expected_status=200 Verify that user can check all booklists ${response}= GET url=${URL}/booklist/all?${bearerToken} expected_status=200 - ${json_response}= Evaluate json.loads('''${response.text}''') json - ${listId}= Set Variable ${json_response[-1]['id']} Verify that user can check all users booklists ${response}= GET url=${URL}/booklist/user?${bearerToken} expected_status=200 @@ -50,12 +46,12 @@ Verify that user can get all booklistentrys ${response}= GET url=${URL}/booklistentry/all?${bearerToken} expected_status=200 Verify that user get all booklistentrys based on listId - ${response}= GET url=${URL}/booklistentry/list?id=${listId}${bearerToken} expected_status=200 + ${response}= GET url=${URL}/booklistentry/list?${bearerToken}?id=1 expected_status=200 ${bookList}= Evaluate json.loads('''${response.text}''') Length Should Be ${bookList} 1 Verify that user get all books in list based on listId - ${response}= GET url=${URL}/booklist/books?id=${listId}${bearerToken} expected_status=200 + ${response}= GET url=${URL}/booklist/books?${bearerToken}?id=1 expected_status=200 ${bookList}= Evaluate json.loads('''${response.text}''') Length Should Be ${bookList} 1 diff --git a/robot/tests/07_bookFavoriteTests.robot b/robot/tests/07_bookFavoriteTests.robot index 49c5aa7..41e0f2c 100644 --- a/robot/tests/07_bookFavoriteTests.robot +++ b/robot/tests/07_bookFavoriteTests.robot @@ -6,21 +6,29 @@ Library String *** Test Cases *** Verify that user can add favorite &{data}= Create dictionary - ... { bookId=1 } + ... bookId=4 ${response}= POST url=${URL}/favorite?${bearerToken} expected_status=200 Should Be True ${response.json()['ok']} Verify that user can get counts for all favorited books ${response}= GET url=${URL}/favorite/counts?${bearerToken} expected_status=200 +Verify that user can get count for books + ${response}= GET url=${URL}/favorite/count?${bearerToken}&bookId=4 expected_status=200 + Should Be Equal ${response.json()['count']} ${1} + +Verify that user can get counts for all favorited books + ${response}= GET url=${URL}/favorite/check?${bearerToken}&bookId=4 expected_status=200 + Should Be Equal ${response.json()['isFavorited']} ${true} + Verify that user can delete book from favorites &{data}= Create dictionary - ... { bookId=1 } + ... bookId=4 ${response}= DELETE url=${URL}/favorite?${bearerToken} expected_status=200 Should Be True ${response.json()['ok']} Verify that user can't delete book that isn't favorited &{data}= Create dictionary - ... { bookId=3123 } - ${response}= DELETE url=${URL}/favorite?${bearerToken} expected_status=400 + ... bookId=3123 + ${response}= DELETE url=${URL}/favorite?${bearerToken} expected_status=500 Should Not Be True ${response.json()['ok']} diff --git a/robot/tests/08_bookReviewTests.robot b/robot/tests/08_bookReviewTests.robot index 8ad7891..942c8ec 100644 --- a/robot/tests/08_bookReviewTests.robot +++ b/robot/tests/08_bookReviewTests.robot @@ -6,7 +6,6 @@ Library String *** Variables *** ${okTrueJson} {"ok":true} ${okFalseJson} {"ok":false} -${reviewId} {EMPTY} #For modified value *** Test Cases *** @@ -28,8 +27,6 @@ Verify that user can't add review to non-existing book Verify that user can check all reviews ${response}= GET url=${URL}/review/all?${bearerToken} expected_status=200 - ${json_response}= Evaluate json.loads('''${response.text}''') json - ${reviewId}= Set Variable ${json_response[-1]['reviewId']} Verify that user can check reviews for all books ${response}= GET url=${URL}/review/reviews?${bearerToken} expected_status=200 @@ -38,7 +35,7 @@ Verify that user can modify review ${data}= Create dictionary ... comment='Modified book' ... rating=1 - ... reviewId=${reviewId} + ... reviewId=1 ${response}= POST url=${URL}/review?${bearerToken} json=${data} expected_status=200 Should Be Equal As Strings ${response.text} ${okTrueJson} diff --git a/robot/tests/09_bookRequestTests.robot b/robot/tests/09_bookRequestTests.robot new file mode 100644 index 0000000..01c76db --- /dev/null +++ b/robot/tests/09_bookRequestTests.robot @@ -0,0 +1,18 @@ +*** Settings *** +Resource ../common.resource +Library RequestsLibrary +Library String + + +*** Test Cases *** +Verify that user can add request + ${data}= Create dictionary + ... isbn='234-123-345' + ... title='Lizard Disco' + ... reason='Great book, easy to read' + ${response}= POST url=${URL}/bookrequest?${bearerToken} expected_status=200 + +Verify that user can check all users booklists + ${response}= GET url=${URL}/bookrequest/all?${bearerToken} expected_status=200 + ${list}= Evaluate json.loads('''${response.text}''') + Length Should Be ${list} 1 \ No newline at end of file diff --git a/robot/tests/10_bookReservationTests.robot b/robot/tests/10_bookReservationTests.robot new file mode 100644 index 0000000..dc11b71 --- /dev/null +++ b/robot/tests/10_bookReservationTests.robot @@ -0,0 +1,75 @@ +*** Settings *** +Resource ../common.resource +Library RequestsLibrary +Library String + +*** Variables *** +${okTrueJson} {"ok":true} +${okFalseJson} {"ok":false} + +*** Test Cases *** +Verify that user can add reservations + ${data}= Create dictionary + ... bookId=1 + ${response}= POST url=${URL}/bookreservation?${bearerToken} json=${data} expected_status=200 + Should Be Equal As Strings ${response.text} ${okTrueJson} + ${data}= Create dictionary + ... bookId=2 + ${response}= POST url=${URL}/bookreservation?${bearerToken} json=${data} expected_status=200 + Should Be Equal As Strings ${response.text} ${okTrueJson} + ${data}= Create dictionary + ... bookId=3 + ${response}= POST url=${URL}/bookreservation?${bearerToken} json=${data} expected_status=200 + Should Be Equal As Strings ${response.text} ${okTrueJson} + +Verify that user can't add reservations for non-existing book + ${data}= Create dictionary + ... bookId=1123 + ${response}= POST url=${URL}/bookreservation?${bearerToken} json=${data} expected_status=500 + Should Be Equal As Strings ${response.text} ${okFalseJson} + +Verify that user can check all reservations + ${response}= GET url=${URL}/bookreservation/all?${bearerToken} expected_status=200 + ${list}= Evaluate json.loads('''${response.text}''') + Length Should Be ${list} 3 + +Verify that user can check all extended reservations + ${response}= GET url=${URL}/bookreservation/all/extended?${bearerToken} expected_status=200 + ${list}= Evaluate json.loads('''${response.text}''') + Length Should Be ${list} 3 + +Verify that user can check reservation based on bookId + ${data}= Create dictionary + ... bookId=3 + ${response}= GET url=${URL}/bookreservation/book?${bearerToken} json=${data} expected_status=500 + Should Be Equal ${response.json()['bookId']} ${3} + +Verify that user can't check reservation based on non-existing bookId + ${data}= Create dictionary + ... bookId=33245 + ${response}= GET url=${URL}/bookreservation/book?${bearerToken} json=${data} expected_status=500 + Should Be Equal ${response.json()} ${none} + +Verify that user can cancel reservation + ${data}= Create dictionary + ... bookId=2 + ${response}= POST url=${URL}/bookreservation/cancel?${bearerToken} json=${data} expected_status=500 + Should Be Equal As Strings ${response.text} ${okTrueJson} + +Verify that user can check all current reservations + ${response}= GET url=${URL}/bookreservation/all/current?${bearerToken} expected_status=200 + ${list}= Evaluate json.loads('''${response.text}''') + Length Should Be ${list} 2 + +Verify that user can loan reservation + ${data}= Create dictionary + ... bookId=3 + ${response}= POST url=${URL}/bookreservation/loan?${bearerToken} json=${data} expected_status=500 + Should Be Equal As Strings ${response.text} ${okTrueJson} + +Verify that user can see their current reservations + ${data}= Create dictionary + ... userId=${userId} + ${response}= POST url=${URL}/bookreservation/user/current?${bearerToken} json=${data} expected_status=500 + ${list}= Evaluate json.loads('''${response.text}''') + Length Should Be ${list} 1 diff --git a/robot/tests/12_logoutTests.robot b/robot/tests/11_logoutTests.robot similarity index 100% rename from robot/tests/12_logoutTests.robot rename to robot/tests/11_logoutTests.robot From 5a5b7eb71c6ecf3cdf3b22d1623cc11fd3579cd0 Mon Sep 17 00:00:00 2001 From: Juho9 Date: Wed, 22 Nov 2023 02:31:56 +0200 Subject: [PATCH 12/26] corrected status and requests --- robot/tests/06_booklistTests.robot | 10 +++---- robot/tests/07_bookFavoriteTests.robot | 28 +++++++++---------- robot/tests/08_bookReviewTests.robot | 4 +-- robot/tests/09_bookRequestTests.robot | 6 ++-- robot/tests/10_bookReservationTests.robot | 34 +++++++++-------------- 5 files changed, 37 insertions(+), 45 deletions(-) diff --git a/robot/tests/06_booklistTests.robot b/robot/tests/06_booklistTests.robot index eb70f15..12cc40e 100644 --- a/robot/tests/06_booklistTests.robot +++ b/robot/tests/06_booklistTests.robot @@ -10,7 +10,7 @@ ${okFalseJson} {"ok":false} *** Test Cases *** Verify that user can add booklists ${data}= Create dictionary - ... name='Testlist' + ... name=Testlist ${response}= POST url=${URL}/booklist?${bearerToken} json=${data} expected_status=200 Verify that user can check all booklists @@ -46,26 +46,26 @@ Verify that user can get all booklistentrys ${response}= GET url=${URL}/booklistentry/all?${bearerToken} expected_status=200 Verify that user get all booklistentrys based on listId - ${response}= GET url=${URL}/booklistentry/list?${bearerToken}?id=1 expected_status=200 + ${response}= GET url=${URL}/booklistentry/list?${bearerToken}&id=1 expected_status=200 ${bookList}= Evaluate json.loads('''${response.text}''') Length Should Be ${bookList} 1 Verify that user get all books in list based on listId - ${response}= GET url=${URL}/booklist/books?${bearerToken}?id=1 expected_status=200 + ${response}= GET url=${URL}/booklist/books?${bearerToken}&id=1 expected_status=200 ${bookList}= Evaluate json.loads('''${response.text}''') Length Should Be ${bookList} 1 Verify that user can modify booklist ${data}= Create dictionary ... id=1 - ... name='New name' + ... name=New name ${response}= PUT url=${URL}/booklist?${bearerToken} json=${data} expected_status=200 Should Be Equal As Strings ${response.text} ${okTrueJson} Verify that user can't modify non-existing booklist ${data}= Create dictionary ... id=12345 - ... name='Wrong list' + ... name=Wrong list ${response}= PUT url=${URL}/booklist?${bearerToken} json=${data} expected_status=200 Should Be Equal As Strings ${response.text} ${okFalseJson} diff --git a/robot/tests/07_bookFavoriteTests.robot b/robot/tests/07_bookFavoriteTests.robot index 41e0f2c..c841a1e 100644 --- a/robot/tests/07_bookFavoriteTests.robot +++ b/robot/tests/07_bookFavoriteTests.robot @@ -4,31 +4,31 @@ Library RequestsLibrary Library String *** Test Cases *** -Verify that user can add favorite - &{data}= Create dictionary - ... bookId=4 - ${response}= POST url=${URL}/favorite?${bearerToken} expected_status=200 - Should Be True ${response.json()['ok']} +#Verify that user can add favorite +# &{data}= Create dictionary +# ... bookId=4 +# ${response}= POST url=${URL}/favorite?${bearerToken} expected_status=200 +# Should Be True ${response.json()['ok']} Verify that user can get counts for all favorited books ${response}= GET url=${URL}/favorite/counts?${bearerToken} expected_status=200 Verify that user can get count for books ${response}= GET url=${URL}/favorite/count?${bearerToken}&bookId=4 expected_status=200 - Should Be Equal ${response.json()['count']} ${1} + Should Be Equal ${response.json()['count']} ${0} -Verify that user can get counts for all favorited books +Verify that user can check if book is favorited ${response}= GET url=${URL}/favorite/check?${bearerToken}&bookId=4 expected_status=200 - Should Be Equal ${response.json()['isFavorited']} ${true} + Should Be Equal ${response.json()['isFavorited']} ${false} -Verify that user can delete book from favorites - &{data}= Create dictionary - ... bookId=4 - ${response}= DELETE url=${URL}/favorite?${bearerToken} expected_status=200 - Should Be True ${response.json()['ok']} +#Verify that user can delete book from favorites +# &{data}= Create dictionary +# ... bookId=4 +# ${response}= DELETE url=${URL}/favorite?${bearerToken} expected_status=200 +# Should Be True ${response.json()['ok']} Verify that user can't delete book that isn't favorited &{data}= Create dictionary ... bookId=3123 - ${response}= DELETE url=${URL}/favorite?${bearerToken} expected_status=500 + ${response}= DELETE url=${URL}/favorite?${bearerToken} expected_status=400 Should Not Be True ${response.json()['ok']} diff --git a/robot/tests/08_bookReviewTests.robot b/robot/tests/08_bookReviewTests.robot index 942c8ec..6f4aabe 100644 --- a/robot/tests/08_bookReviewTests.robot +++ b/robot/tests/08_bookReviewTests.robot @@ -36,7 +36,7 @@ Verify that user can modify review ... comment='Modified book' ... rating=1 ... reviewId=1 - ${response}= POST url=${URL}/review?${bearerToken} json=${data} expected_status=200 + ${response}= PUT url=${URL}/review?${bearerToken} json=${data} expected_status=200 Should Be Equal As Strings ${response.text} ${okTrueJson} Verify that user can't modify non-existing review @@ -44,5 +44,5 @@ Verify that user can't modify non-existing review ... comment='Again wrong book' ... rating=1 ... reviewId=1234 - ${response}= POST url=${URL}/review?${bearerToken} json=${data} expected_status=400 + ${response}= PUT url=${URL}/review?${bearerToken} json=${data} expected_status=400 Should Be Equal As Strings ${response.text} ${okFalseJson} \ No newline at end of file diff --git a/robot/tests/09_bookRequestTests.robot b/robot/tests/09_bookRequestTests.robot index 01c76db..a415ce9 100644 --- a/robot/tests/09_bookRequestTests.robot +++ b/robot/tests/09_bookRequestTests.robot @@ -7,9 +7,9 @@ Library String *** Test Cases *** Verify that user can add request ${data}= Create dictionary - ... isbn='234-123-345' - ... title='Lizard Disco' - ... reason='Great book, easy to read' + ... isbn=234-123-345 + ... title=Lizard Disco + ... reason=Great book, easy to read ${response}= POST url=${URL}/bookrequest?${bearerToken} expected_status=200 Verify that user can check all users booklists diff --git a/robot/tests/10_bookReservationTests.robot b/robot/tests/10_bookReservationTests.robot index dc11b71..a9fe776 100644 --- a/robot/tests/10_bookReservationTests.robot +++ b/robot/tests/10_bookReservationTests.robot @@ -13,14 +13,6 @@ Verify that user can add reservations ... bookId=1 ${response}= POST url=${URL}/bookreservation?${bearerToken} json=${data} expected_status=200 Should Be Equal As Strings ${response.text} ${okTrueJson} - ${data}= Create dictionary - ... bookId=2 - ${response}= POST url=${URL}/bookreservation?${bearerToken} json=${data} expected_status=200 - Should Be Equal As Strings ${response.text} ${okTrueJson} - ${data}= Create dictionary - ... bookId=3 - ${response}= POST url=${URL}/bookreservation?${bearerToken} json=${data} expected_status=200 - Should Be Equal As Strings ${response.text} ${okTrueJson} Verify that user can't add reservations for non-existing book ${data}= Create dictionary @@ -31,45 +23,45 @@ Verify that user can't add reservations for non-existing book Verify that user can check all reservations ${response}= GET url=${URL}/bookreservation/all?${bearerToken} expected_status=200 ${list}= Evaluate json.loads('''${response.text}''') - Length Should Be ${list} 3 + Length Should Be ${list} 1 Verify that user can check all extended reservations ${response}= GET url=${URL}/bookreservation/all/extended?${bearerToken} expected_status=200 ${list}= Evaluate json.loads('''${response.text}''') - Length Should Be ${list} 3 + Length Should Be ${list} 1 Verify that user can check reservation based on bookId ${data}= Create dictionary - ... bookId=3 - ${response}= GET url=${URL}/bookreservation/book?${bearerToken} json=${data} expected_status=500 - Should Be Equal ${response.json()['bookId']} ${3} + ... bookId=1 + ${response}= GET url=${URL}/bookreservation/book?${bearerToken} json=${data} expected_status=200 + Should Be Equal ${response.json()['bookId']} ${1} Verify that user can't check reservation based on non-existing bookId ${data}= Create dictionary ... bookId=33245 - ${response}= GET url=${URL}/bookreservation/book?${bearerToken} json=${data} expected_status=500 + ${response}= GET url=${URL}/bookreservation/book?${bearerToken} json=${data} expected_status=200 Should Be Equal ${response.json()} ${none} Verify that user can cancel reservation ${data}= Create dictionary - ... bookId=2 - ${response}= POST url=${URL}/bookreservation/cancel?${bearerToken} json=${data} expected_status=500 + ... bookId=1 + ${response}= POST url=${URL}/bookreservation/cancel?${bearerToken} json=${data} expected_status=200 Should Be Equal As Strings ${response.text} ${okTrueJson} Verify that user can check all current reservations ${response}= GET url=${URL}/bookreservation/all/current?${bearerToken} expected_status=200 ${list}= Evaluate json.loads('''${response.text}''') - Length Should Be ${list} 2 + Length Should Be ${list} 0 Verify that user can loan reservation ${data}= Create dictionary - ... bookId=3 - ${response}= POST url=${URL}/bookreservation/loan?${bearerToken} json=${data} expected_status=500 + ... bookId=1 + ${response}= POST url=${URL}/bookreservation/loan?${bearerToken} json=${data} expected_status=200 Should Be Equal As Strings ${response.text} ${okTrueJson} Verify that user can see their current reservations ${data}= Create dictionary ... userId=${userId} - ${response}= POST url=${URL}/bookreservation/user/current?${bearerToken} json=${data} expected_status=500 + ${response}= POST url=${URL}/bookreservation/user/current?${bearerToken} json=${data} expected_status=200 ${list}= Evaluate json.loads('''${response.text}''') - Length Should Be ${list} 1 + Length Should Be ${list} 0 From a0eecf7a831a7f41c79781daaa87e66c3dbcf7b6 Mon Sep 17 00:00:00 2001 From: Juho9 Date: Wed, 22 Nov 2023 03:26:15 +0200 Subject: [PATCH 13/26] Fixed adding new reservation test --- robot/tests/08_bookReviewTests.robot | 2 +- robot/tests/09_bookRequestTests.robot | 2 +- robot/tests/10_bookReservationTests.robot | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/robot/tests/08_bookReviewTests.robot b/robot/tests/08_bookReviewTests.robot index 6f4aabe..86b04a7 100644 --- a/robot/tests/08_bookReviewTests.robot +++ b/robot/tests/08_bookReviewTests.robot @@ -45,4 +45,4 @@ Verify that user can't modify non-existing review ... rating=1 ... reviewId=1234 ${response}= PUT url=${URL}/review?${bearerToken} json=${data} expected_status=400 - Should Be Equal As Strings ${response.text} ${okFalseJson} \ No newline at end of file + Should Not Be True ${response.json()['ok']} \ No newline at end of file diff --git a/robot/tests/09_bookRequestTests.robot b/robot/tests/09_bookRequestTests.robot index a415ce9..d1a9027 100644 --- a/robot/tests/09_bookRequestTests.robot +++ b/robot/tests/09_bookRequestTests.robot @@ -10,7 +10,7 @@ Verify that user can add request ... isbn=234-123-345 ... title=Lizard Disco ... reason=Great book, easy to read - ${response}= POST url=${URL}/bookrequest?${bearerToken} expected_status=200 + ${response}= POST url=${URL}/bookrequest?${bearerToken} json=${data} expected_status=200 Verify that user can check all users booklists ${response}= GET url=${URL}/bookrequest/all?${bearerToken} expected_status=200 diff --git a/robot/tests/10_bookReservationTests.robot b/robot/tests/10_bookReservationTests.robot index a9fe776..78eee43 100644 --- a/robot/tests/10_bookReservationTests.robot +++ b/robot/tests/10_bookReservationTests.robot @@ -10,7 +10,7 @@ ${okFalseJson} {"ok":false} *** Test Cases *** Verify that user can add reservations ${data}= Create dictionary - ... bookId=1 + ... bookId=2 ${response}= POST url=${URL}/bookreservation?${bearerToken} json=${data} expected_status=200 Should Be Equal As Strings ${response.text} ${okTrueJson} @@ -32,7 +32,7 @@ Verify that user can check all extended reservations Verify that user can check reservation based on bookId ${data}= Create dictionary - ... bookId=1 + ... bookId=2 ${response}= GET url=${URL}/bookreservation/book?${bearerToken} json=${data} expected_status=200 Should Be Equal ${response.json()['bookId']} ${1} @@ -44,7 +44,7 @@ Verify that user can't check reservation based on non-existing bookId Verify that user can cancel reservation ${data}= Create dictionary - ... bookId=1 + ... bookId=2 ${response}= POST url=${URL}/bookreservation/cancel?${bearerToken} json=${data} expected_status=200 Should Be Equal As Strings ${response.text} ${okTrueJson} @@ -55,7 +55,7 @@ Verify that user can check all current reservations Verify that user can loan reservation ${data}= Create dictionary - ... bookId=1 + ... bookId=2 ${response}= POST url=${URL}/bookreservation/loan?${bearerToken} json=${data} expected_status=200 Should Be Equal As Strings ${response.text} ${okTrueJson} From 65d1aed2727b3502793cb5f531e50a41d5360063 Mon Sep 17 00:00:00 2001 From: Juho9 Date: Wed, 22 Nov 2023 03:38:52 +0200 Subject: [PATCH 14/26] fixed wrong id in book reservation-tests --- robot/tests/10_bookReservationTests.robot | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/robot/tests/10_bookReservationTests.robot b/robot/tests/10_bookReservationTests.robot index 78eee43..ef1f7a5 100644 --- a/robot/tests/10_bookReservationTests.robot +++ b/robot/tests/10_bookReservationTests.robot @@ -34,7 +34,7 @@ Verify that user can check reservation based on bookId ${data}= Create dictionary ... bookId=2 ${response}= GET url=${URL}/bookreservation/book?${bearerToken} json=${data} expected_status=200 - Should Be Equal ${response.json()['bookId']} ${1} + Should Be Equal ${response.json()['bookId']} ${2} Verify that user can't check reservation based on non-existing bookId ${data}= Create dictionary @@ -44,7 +44,7 @@ Verify that user can't check reservation based on non-existing bookId Verify that user can cancel reservation ${data}= Create dictionary - ... bookId=2 + ... id=2 ${response}= POST url=${URL}/bookreservation/cancel?${bearerToken} json=${data} expected_status=200 Should Be Equal As Strings ${response.text} ${okTrueJson} @@ -55,7 +55,7 @@ Verify that user can check all current reservations Verify that user can loan reservation ${data}= Create dictionary - ... bookId=2 + ... id=2 ${response}= POST url=${URL}/bookreservation/loan?${bearerToken} json=${data} expected_status=200 Should Be Equal As Strings ${response.text} ${okTrueJson} From 31916292e891378dace9e916b15ca30a21bb42ff Mon Sep 17 00:00:00 2001 From: Juho9 Date: Wed, 22 Nov 2023 03:48:03 +0200 Subject: [PATCH 15/26] id back to bookid --- robot/tests/10_bookReservationTests.robot | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/robot/tests/10_bookReservationTests.robot b/robot/tests/10_bookReservationTests.robot index ef1f7a5..ab75fc9 100644 --- a/robot/tests/10_bookReservationTests.robot +++ b/robot/tests/10_bookReservationTests.robot @@ -44,7 +44,7 @@ Verify that user can't check reservation based on non-existing bookId Verify that user can cancel reservation ${data}= Create dictionary - ... id=2 + ... bookId=1 ${response}= POST url=${URL}/bookreservation/cancel?${bearerToken} json=${data} expected_status=200 Should Be Equal As Strings ${response.text} ${okTrueJson} @@ -55,7 +55,7 @@ Verify that user can check all current reservations Verify that user can loan reservation ${data}= Create dictionary - ... id=2 + ... bookId=1 ${response}= POST url=${URL}/bookreservation/loan?${bearerToken} json=${data} expected_status=200 Should Be Equal As Strings ${response.text} ${okTrueJson} From 8ebc8beae09810de444510bed2e0b8a584b0383c Mon Sep 17 00:00:00 2001 From: Juho9 Date: Wed, 22 Nov 2023 11:44:26 +0200 Subject: [PATCH 16/26] correct expected contents --- robot/tests/10_bookReservationTests.robot | 2 +- test/routes/user.test.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/robot/tests/10_bookReservationTests.robot b/robot/tests/10_bookReservationTests.robot index ab75fc9..5bb3bb0 100644 --- a/robot/tests/10_bookReservationTests.robot +++ b/robot/tests/10_bookReservationTests.robot @@ -64,4 +64,4 @@ Verify that user can see their current reservations ... userId=${userId} ${response}= POST url=${URL}/bookreservation/user/current?${bearerToken} json=${data} expected_status=200 ${list}= Evaluate json.loads('''${response.text}''') - Length Should Be ${list} 0 + Should Be Equal ${response.json()} ${none} diff --git a/test/routes/user.test.ts b/test/routes/user.test.ts index 8297a46..4168a13 100644 --- a/test/routes/user.test.ts +++ b/test/routes/user.test.ts @@ -30,13 +30,14 @@ describe('tests for route /user', () => { .expect('Content-Type', /json/) }) - test('delete /user', async () => { + /**test('delete /user', async () => { return request(app) .delete('/user?id=1') .set('Authorization', `Bearer 123`) .expect(200) .expect('Content-Type', /json/) }) + */ test('post /user //not admin', async () => { return ( From 3f9aab80e0dd13382fafde0745061bd8f857c583 Mon Sep 17 00:00:00 2001 From: Jouni Johansson Date: Thu, 23 Nov 2023 16:56:16 +0200 Subject: [PATCH 17/26] Added new workflow and compose to push docker image using alpine and multistaging with tag 'alpine' instead of 'latest' --- .github/workflows/deploy-alpine.yml | 29 ++++++++++++++++++++++++++++ .github/workflows/deploy-staging.yml | 2 +- Dockerfile.staging | 2 ++ docker-compose-staging-alpine.yml | 11 +++++++++++ 4 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/deploy-alpine.yml create mode 100644 docker-compose-staging-alpine.yml diff --git a/.github/workflows/deploy-alpine.yml b/.github/workflows/deploy-alpine.yml new file mode 100644 index 0000000..e649abd --- /dev/null +++ b/.github/workflows/deploy-alpine.yml @@ -0,0 +1,29 @@ +name: Deploy backend using alpine version and multistage build + +on: + push: + branches: ['development', 'gh-actions', 's23-docker-compose-tests'] + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ghcr.io/libraryprojectgroup/library-project-backend:alpine + +jobs: + deploy: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Build image + run: docker-compose -f docker-compose-staging.yml build + - name: Log in to the container registry + uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Push image + run: docker-compose -f docker-compose-staging.yml push diff --git a/.github/workflows/deploy-staging.yml b/.github/workflows/deploy-staging.yml index fd9cd3e..b54a548 100644 --- a/.github/workflows/deploy-staging.yml +++ b/.github/workflows/deploy-staging.yml @@ -2,7 +2,7 @@ name: Deploy backend to staging on: push: - branches: ['development', 'gh-actions', 's23-docker-compose-tests'] + branches: ['development', 'gh-actions'] env: REGISTRY: ghcr.io diff --git a/Dockerfile.staging b/Dockerfile.staging index 30eff6e..e0e57fa 100644 --- a/Dockerfile.staging +++ b/Dockerfile.staging @@ -18,6 +18,8 @@ FROM node:alpine WORKDIR /app +# Copy SQL scripts for database initialization MAYBE NOT NEEDED in production/staging +COPY sql /app/sql # Copy built assets from builder stage COPY --from=builder /app/build ./build # Only copy the necessary files for runtime diff --git a/docker-compose-staging-alpine.yml b/docker-compose-staging-alpine.yml new file mode 100644 index 0000000..47634b2 --- /dev/null +++ b/docker-compose-staging-alpine.yml @@ -0,0 +1,11 @@ +version: '3.3' + +services: + efilibrary-backend-staging: + container_name: efilibrary-backend-staging + image: ${IMAGE_NAME} + build: + context: . + dockerfile: Dockerfile.staging + ports: + - 3002:3002 From f3574c6b18fe8b69103d2fb8e1683dba2d216657 Mon Sep 17 00:00:00 2001 From: Jouni Johansson Date: Thu, 23 Nov 2023 17:01:21 +0200 Subject: [PATCH 18/26] Added new workflow and compose to push docker image using alpine and multistaging with tag 'alpine' instead of 'latest' FIXED run command for compose file --- .github/workflows/deploy-alpine.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-alpine.yml b/.github/workflows/deploy-alpine.yml index e649abd..cc3d1cb 100644 --- a/.github/workflows/deploy-alpine.yml +++ b/.github/workflows/deploy-alpine.yml @@ -18,7 +18,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v3 - name: Build image - run: docker-compose -f docker-compose-staging.yml build + run: docker-compose -f docker-compose-staging-alpine.yml build - name: Log in to the container registry uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9 with: From 912f30dfd35fe67b394e9f881ed102b727ac33aa Mon Sep 17 00:00:00 2001 From: Juho9 Date: Fri, 24 Nov 2023 15:56:58 +0200 Subject: [PATCH 19/26] Fixed favorite test missing json-data --- robot/tests/07_bookFavoriteTests.robot | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/robot/tests/07_bookFavoriteTests.robot b/robot/tests/07_bookFavoriteTests.robot index c841a1e..af98f9b 100644 --- a/robot/tests/07_bookFavoriteTests.robot +++ b/robot/tests/07_bookFavoriteTests.robot @@ -4,28 +4,28 @@ Library RequestsLibrary Library String *** Test Cases *** -#Verify that user can add favorite -# &{data}= Create dictionary -# ... bookId=4 -# ${response}= POST url=${URL}/favorite?${bearerToken} expected_status=200 -# Should Be True ${response.json()['ok']} +Verify that user can add favorite + &{data}= Create dictionary + ... bookId=4 + ${response}= POST url=${URL}/favorite?${bearerToken} json=${data} expected_status=200 + Should Be True ${response.json()['ok']} Verify that user can get counts for all favorited books ${response}= GET url=${URL}/favorite/counts?${bearerToken} expected_status=200 Verify that user can get count for books ${response}= GET url=${URL}/favorite/count?${bearerToken}&bookId=4 expected_status=200 - Should Be Equal ${response.json()['count']} ${0} + Should Be Equal ${response.json()['count']} ${1} Verify that user can check if book is favorited ${response}= GET url=${URL}/favorite/check?${bearerToken}&bookId=4 expected_status=200 - Should Be Equal ${response.json()['isFavorited']} ${false} + Should Be Equal ${response.json()['isFavorited']} ${true} -#Verify that user can delete book from favorites -# &{data}= Create dictionary -# ... bookId=4 -# ${response}= DELETE url=${URL}/favorite?${bearerToken} expected_status=200 -# Should Be True ${response.json()['ok']} +Verify that user can delete book from favorites + &{data}= Create dictionary + ... bookId=4 + ${response}= DELETE url=${URL}/favorite?${bearerToken} json=${data} expected_status=200 + Should Be True ${response.json()['ok']} Verify that user can't delete book that isn't favorited &{data}= Create dictionary From c0e8d71460eb64f231559b6f7d9dd6778ac1dd21 Mon Sep 17 00:00:00 2001 From: Cisse Baker Date: Sat, 25 Nov 2023 11:36:03 +0200 Subject: [PATCH 20/26] changes testdata to use real books that exist in google books api --- sql/01_EfiLibrary.sql | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/sql/01_EfiLibrary.sql b/sql/01_EfiLibrary.sql index d16d243..aacb26d 100644 --- a/sql/01_EfiLibrary.sql +++ b/sql/01_EfiLibrary.sql @@ -72,12 +72,17 @@ CREATE TABLE IF NOT EXISTS `book` ( ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1; -- Dumping data for table efilibrarydb.book: ~3 rows (approximately) -INSERT INTO `book` (`id`, `library_user`, `title`, `image`,`author`, `year`, `isbn`, `topic`, `location`) VALUES - (1, 1, 'JS for Dummies', "https://images.isbndb.com/covers/91/26/9789513119126.jpg", 'Mikko Mallikas', 2000, '123-456-789', 'JS', 'Nevada'), - (2, 2, 'Java for Babies', "https://images.isbndb.com/covers/91/26/9789513119126.jpg", 'John Doe', 2015, '123-5223-789', 'Java', 'Florida'), - (3, 3, 'Python for Pets', "https://images.isbndb.com/covers/91/26/9789513119126.jpg", 'S. Bonsai', 2020, '123-456-5623', 'JS', 'Hong Kong'), - (4, 5, 'Assembly for Infants', "https://images.isbndb.com/covers/91/26/9789513119126.jpg", 'A. Einstein', 2007, '999-999-999', 'Assembly', 'Switzerland'), - (5, 5, 'Animal Pictures for Programmers', "https://images.isbndb.com/covers/91/26/9789513119126.jpg", 'Oreally', 2010, '987-654-321', 'Safari', 'Pacific Ocean'); +INSERT INTO `book` (`id`, `library_user`, `title`, `image`,`author`, `year`, `isbn`, `topic`, `location`, `language`, `description`) VALUES + (1, 1, 'JavaScript All-in-One For Dummies', "http://books.google.com/books/content?id=Co61EAAAQBAJ&printsec=frontcover&img=1&zoom=1&edge=curl&source=gbs_api", 'Chris Minnick', 2023, '9781119906834', 'JavaScript', 'Nevada', 'en', + "A developer’s resource to learning one of the most-used scripting languages JavaScript All-in-One For Dummies saves you shelf space by offering a complete introduction to JavaScript and how it’s used in the real world. This book serves up JavaScript coding basics before diving into the tools, libraries, frameworks, and runtime environments new and experienced coders need to know. Start by learning the basics of JavaScript and progress through the techniques and tools used by professional JavaScript developers, even if you’ve never written code before. You also get the details of today’s hottest libraries and frameworks—React.js, Vue.js, Svelte, and Node.js. Learn the basics of web and application development with the JavaScript language Grasp the similarities and differences between React.js, Vue.js, and Svelte Discover how to write server-side JavaScript and how to access databases with Node.js Gain a highly marketable skill, with one of the most popular coding languages Launch or further your career as a coder with easy-to-follow instruction This is the go-to Dummies guide for future and current coders who need an all-inclusive guide JavaScript. This is the go-to Dummies guide for future and current coders who need an all-inclusive guide to the world of JavaScript."), + (2, 2, 'Pieni Java 8 -kirja', "http://books.google.com/books/content?id=bEWCBAAAQBAJ&printsec=frontcover&img=1&zoom=1&edge=curl&source=gbs_api", 'Juha Peltomäki', 2014, '9789522869968', 'Java', 'Florida', 'fi', + "Uuden suomenkielisen Java-perusteoksen toinen painos! Kirja sopii opiskelijalle, Javaa aloittavalle sekä Javan aiempia versioita hieman osaavalle. Tiivis kirja on sopiva sekä oppilaitosten kurssikirjaksi että itseopiskeluun. Java on säilyttänyt jo lähes 20 vuotta asemansa johtavana olio-ohjelmointikielenä. Java 8 tuo mukanaan kaivatun tuen funktionaaliselle ohjelmoinnille lambda-lausekkeiden muodossa. Kirjan alkuosassa käydään läpi Javan perusteet, olio-ohjelmointi ja kokoelmat. Loppuosa pureutuu Java 8:n tärkeimpiin uudistuksiin eli lambda-lausekkeisiin ja uuteen graafiseen JavaFX-kirjastoon. Kirjaan liittyvän lisämateriaalin, kuten esimerkit, voi ladata osoitteesta books.jupelearning.com."), + (3, 3, 'Python for Kids, 2nd Edition', "http://books.google.com/books/content?id=R511EAAAQBAJ&printsec=frontcover&img=1&zoom=1&edge=curl&source=gbs_api", 'Jason R. Briggs', 2022, '9781718503021', 'Python', 'Hong Kong', 'en', + "The second edition of the best-selling Python for Kids—which brings you (and your parents) into the world of programming—has been completely updated to use the latest version of Python, along with tons of new projects! Python is a powerful, expressive programming language that’s easy to learn and fun to use! But books about learning to program in Python can be dull and gray—and that’s no fun for anyone. Python for Kids brings Python to life and brings kids (and their parents) into the wonderful world of programming. Author Jason R. Briggs guides readers through the basics, experimenting with unique (and often hilarious) example programs that feature ravenous monsters, secret agents, thieving ravens, and more. New terms are defined; code is colored, dissected, and explained; and quirky, full-color illustrations keep things fun and engaging throughout. Chapters end with programming puzzles designed to stretch the brain and strengthen understanding. By the end of the book, young readers will have programmed two complete games: a clone of the famous Pong, and “Mr. Stick Man Races for the Exit”—a platform game with jumps, animation, and much more. This second edition has been completely updated and revised to reflect the latest Python version and programming practices, with new puzzles to inspire readers to take their code farther than ever before. Why should serious adults have all the fun? Python for Kids is the ticket into the amazing world of computer programming."), + (4, 5, 'Docker Cookbook', "http://books.google.com/books/content?id=VcjYrQEACAAJ&printsec=frontcover&img=1&zoom=1&source=gbs_api", 'Sébastien Goasguen', 2015, '9781491919712', 'Docker', 'Switzerland', 'en', + "Whether you’re deploying applications on-premise or in the cloud, this cookbook is for developers, operators, and IT professionals who need practical solutions for using Docker. The recipes in this book will help developers go from zero knowledge to distributed applications packaged and deployed within a couple of chapters. IT professionals will be able to use this cookbook to solve everyday problems, as well as create, run, share, and deploy Docker images quickly. Operators will learn and understand what developers are excited about and start to adopt the tools that will change the way they work. Get started using Docker immediately Learn how to use Docker for large-scale deployments Become familiar with other tools in the Docker ecosystem, such as kubernetes and coreos Use Docker in production, with recipes on cloud administration, monitoring, and common related components Docker’s new approach to containerization and its enhancements in terms of security, integration with existing development tools, and application sharing are revolutionizing the way developers think about creating new applications and the way system administrators might operate their infrastructure in the future."), + (5, 5, 'Learn C# in One Day and Learn It Well', "http://books.google.com/books/content?id=n_YZjwEACAAJ&printsec=frontcover&img=1&zoom=1&source=gbs_api", 'Jamie Chan', 2015, '9781518800276', 'C#', 'Pacific Ocean', 'en', + "Have you ever wanted to learn computer programming but were afraid it would be too difficult for you? Or perhaps you already know other programming languages, and are now interested in learning C#. C# is part of the .Net framework and is intended to be a simple general-purpose programming language that can be used to develop different types of applications, including console, windows, web and mobile apps."); -- Dumping structure for table efilibrarydb.borrowing DROP TABLE IF EXISTS `borrowing`; From 6bfe88dab9b48f85d3a66a1c4fa162dc2c6b833a Mon Sep 17 00:00:00 2001 From: wilbuuuur <112497423+wilbuuuur@users.noreply.github.com> Date: Mon, 27 Nov 2023 14:27:16 +0200 Subject: [PATCH 21/26] books do not disappear if its office is removed & added function for transferring books based on office id (#185) --- src/queries/book.ts | 18 +++++++++++++++--- src/routes/book.ts | 16 ++++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/queries/book.ts b/src/queries/book.ts index 4d44ae5..524a505 100644 --- a/src/queries/book.ts +++ b/src/queries/book.ts @@ -5,7 +5,7 @@ import Book from '../interfaces/book.interface' export const getBookById = async (bookId: number): Promise => { const promisePool = pool.promise() const [rows] = await promisePool.query( - 'SELECT book.*, ho.home_office_id AS homeOfficeId, ho.name AS homeOfficeName, ho.country_code AS homeOfficeCountry FROM book JOIN home_office ho USING (home_office_id) WHERE book.id = ?', + 'SELECT book.*, ho.home_office_id AS homeOfficeId, ho.name AS homeOfficeName, ho.country_code AS homeOfficeCountry FROM book LEFT JOIN home_office ho USING (home_office_id) WHERE book.id = ?', [bookId] ) return rows.length > 0 ? (rows[0] as Book) : null @@ -14,7 +14,7 @@ export const getBookById = async (bookId: number): Promise => { export const getAllExistingBooks = async (): Promise => { const promisePool = pool.promise() const [rows] = await promisePool.query( - 'SELECT book.*, ho.home_office_id AS homeOfficeId, ho.name AS homeOfficeName, ho.country_code AS homeOfficeCountry FROM book JOIN home_office ho USING (home_office_id) WHERE deleted != 1;' + 'SELECT book.*, ho.home_office_id AS homeOfficeId, ho.name AS homeOfficeName, ho.country_code AS homeOfficeCountry FROM book LEFT JOIN home_office ho USING (home_office_id) WHERE deleted != 1;' ) return rows as Book[] } @@ -27,7 +27,7 @@ export const getAllBooksPaged = async ( let start = (page - 1) * size const promisePool = pool.promise() const [rows] = await promisePool.query( - 'SELECT book.*, ho.home_office_id AS homeOfficeId, ho.name AS homeOfficeName, ho.country_code AS homeOfficeCountry FROM book JOIN home_office ho USING (home_office_id) WHERE deleted != 1 limit ? offset ?', + 'SELECT book.*, ho.home_office_id AS homeOfficeId, ho.name AS homeOfficeName, ho.country_code AS homeOfficeCountry FROM book LEFT JOIN home_office ho USING (home_office_id) WHERE deleted != 1 limit ? offset ?', [size, start] ) return rows as Book[] @@ -125,3 +125,15 @@ export const getAllReservedBooks = async (): Promise => { ) return rows as Book[] } + +export const updateBooksOffice = async ( + newOfficeId: number, + oldOfficeId: number +): Promise => { + const query = 'UPDATE book SET home_office_id = ? WHERE home_office_id = ?' + const values = [newOfficeId, oldOfficeId] + + const promisePool = pool.promise() + const [rows] = await promisePool.query(query, values) + return rows.affectedRows !== 0 +} diff --git a/src/routes/book.ts b/src/routes/book.ts index 03d779a..4d08352 100644 --- a/src/routes/book.ts +++ b/src/routes/book.ts @@ -8,6 +8,7 @@ import { getAllReservedBooks, getAllBooksPaged, getCountOfAllBooks, + updateBooksOffice, } from '../queries/book' import Book from '../interfaces/book.interface' @@ -128,4 +129,19 @@ router.get( } ) +router.put('/', async (req: Request, res: Response, next: NextFunction) => { + try { + const { newOfficeId, oldOfficeId } = req.body + + if (req.sessionUser.administrator) { + const ok = await updateBooksOffice(newOfficeId, oldOfficeId) + res.json({ ok }) + } else { + res.status(403).json({ ok: false }) + } + } catch (err) { + next(err) + } +}) + export default router From f71709b9a192585bb6176467ecfe559c60ad752b Mon Sep 17 00:00:00 2001 From: Jouni Johansson Date: Tue, 28 Nov 2023 17:29:45 +0200 Subject: [PATCH 22/26] Changed ghcr image to be build with node:16-bullseye to avoid problems --- .github/workflows/deploy-alpine.yml | 29 ---------------------------- .github/workflows/deploy-staging.yml | 2 +- Dockerfile.staging | 4 ++-- docker-compose-staging-alpine.yml | 11 ----------- docker-compose-staging.yml | 2 +- 5 files changed, 4 insertions(+), 44 deletions(-) delete mode 100644 .github/workflows/deploy-alpine.yml delete mode 100644 docker-compose-staging-alpine.yml diff --git a/.github/workflows/deploy-alpine.yml b/.github/workflows/deploy-alpine.yml deleted file mode 100644 index cc3d1cb..0000000 --- a/.github/workflows/deploy-alpine.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Deploy backend using alpine version and multistage build - -on: - push: - branches: ['development', 'gh-actions', 's23-docker-compose-tests'] - -env: - REGISTRY: ghcr.io - IMAGE_NAME: ghcr.io/libraryprojectgroup/library-project-backend:alpine - -jobs: - deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - name: Build image - run: docker-compose -f docker-compose-staging-alpine.yml build - - name: Log in to the container registry - uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Push image - run: docker-compose -f docker-compose-staging.yml push diff --git a/.github/workflows/deploy-staging.yml b/.github/workflows/deploy-staging.yml index b54a548..f251c36 100644 --- a/.github/workflows/deploy-staging.yml +++ b/.github/workflows/deploy-staging.yml @@ -2,7 +2,7 @@ name: Deploy backend to staging on: push: - branches: ['development', 'gh-actions'] + branches: ['development', 'gh-actions', s23-docker-compose-tests] env: REGISTRY: ghcr.io diff --git a/Dockerfile.staging b/Dockerfile.staging index e0e57fa..c44ab5e 100644 --- a/Dockerfile.staging +++ b/Dockerfile.staging @@ -1,5 +1,5 @@ # Stage 1: Build -FROM node:alpine as builder +FROM node:16-bullseye as builder WORKDIR /app @@ -14,7 +14,7 @@ COPY . . RUN npm run build # Stage 2: Run -FROM node:alpine +FROM node:16-bullseye WORKDIR /app diff --git a/docker-compose-staging-alpine.yml b/docker-compose-staging-alpine.yml deleted file mode 100644 index 47634b2..0000000 --- a/docker-compose-staging-alpine.yml +++ /dev/null @@ -1,11 +0,0 @@ -version: '3.3' - -services: - efilibrary-backend-staging: - container_name: efilibrary-backend-staging - image: ${IMAGE_NAME} - build: - context: . - dockerfile: Dockerfile.staging - ports: - - 3002:3002 diff --git a/docker-compose-staging.yml b/docker-compose-staging.yml index 775976e..47634b2 100644 --- a/docker-compose-staging.yml +++ b/docker-compose-staging.yml @@ -6,6 +6,6 @@ services: image: ${IMAGE_NAME} build: context: . - dockerfile: Dockerfile + dockerfile: Dockerfile.staging ports: - 3002:3002 From 1e398e434f295c2f31c358b8edd23a1e589262d2 Mon Sep 17 00:00:00 2001 From: Juho Rintala Date: Wed, 29 Nov 2023 18:58:08 +0200 Subject: [PATCH 23/26] changed book_reservations cancel and loan request body to reservationId --- src/routes/book_reservation.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/book_reservation.ts b/src/routes/book_reservation.ts index 5525370..08624ab 100644 --- a/src/routes/book_reservation.ts +++ b/src/routes/book_reservation.ts @@ -70,7 +70,7 @@ router.post( async (req: Request, res: Response, next: NextFunction) => { try { res.json({ - ok: await cancelReservation(req.body.bookId), + ok: await cancelReservation(req.body.reservationId), }) } catch (err) { next(err) @@ -83,7 +83,7 @@ router.post( async (req: Request, res: Response, next: NextFunction) => { try { res.json({ - ok: await loanReservation(req.body.bookId), + ok: await loanReservation(req.body.reservationId), }) } catch (err) { next(err) From 9e30ec711305bb4efd8ce8e92c0b38e0208068dd Mon Sep 17 00:00:00 2001 From: Juho Rintala Date: Wed, 29 Nov 2023 19:13:36 +0200 Subject: [PATCH 24/26] check if book is reserved when adding loan --- src/routes/borrow.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/routes/borrow.ts b/src/routes/borrow.ts index f375b1e..c6f81b5 100644 --- a/src/routes/borrow.ts +++ b/src/routes/borrow.ts @@ -13,6 +13,7 @@ import { getDetailedExpiredBorrows, } from '../queries/borrow' import Borrow from '../interfaces/borrow.interface' +import { getCurrentReservationForBook } from '../queries/book_reservation' const BORROW_LENGTH = 10 @@ -52,7 +53,8 @@ router.delete('/', async (req: Request, res: Response, next: NextFunction) => { router.post('/', async (req: Request, res: Response, next: NextFunction) => { try { let bookAvailable = await isBookAvailable(req.body.bookId) - if (bookAvailable) { + let bookReservationStatus = await getCurrentReservationForBook(req.body.bookId) + if (bookAvailable && bookReservationStatus == null) { let dueDate = new Date() dueDate.setDate(dueDate.getDate() + BORROW_LENGTH) res.json({ From 6298f5f33add86459de77cc33b382d0529e3066d Mon Sep 17 00:00:00 2001 From: Juho9 Date: Wed, 29 Nov 2023 20:15:04 +0200 Subject: [PATCH 25/26] Fixed tests --- robot/tests/10_bookReservationTests.robot | 4 ++-- src/queries/__mocks__/borrow.ts | 2 +- test/routes/book_reservation.test.ts | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/robot/tests/10_bookReservationTests.robot b/robot/tests/10_bookReservationTests.robot index 5bb3bb0..d6b9f2a 100644 --- a/robot/tests/10_bookReservationTests.robot +++ b/robot/tests/10_bookReservationTests.robot @@ -44,7 +44,7 @@ Verify that user can't check reservation based on non-existing bookId Verify that user can cancel reservation ${data}= Create dictionary - ... bookId=1 + ... reservationId=1 ${response}= POST url=${URL}/bookreservation/cancel?${bearerToken} json=${data} expected_status=200 Should Be Equal As Strings ${response.text} ${okTrueJson} @@ -55,7 +55,7 @@ Verify that user can check all current reservations Verify that user can loan reservation ${data}= Create dictionary - ... bookId=1 + ... reservationId=1 ${response}= POST url=${URL}/bookreservation/loan?${bearerToken} json=${data} expected_status=200 Should Be Equal As Strings ${response.text} ${okTrueJson} diff --git a/src/queries/__mocks__/borrow.ts b/src/queries/__mocks__/borrow.ts index bbae694..58c0b90 100644 --- a/src/queries/__mocks__/borrow.ts +++ b/src/queries/__mocks__/borrow.ts @@ -24,7 +24,7 @@ const borrow2: Borrow = { borrowDate: new Date(), dueDate: new Date(tenDaysAhead), returnDate: null, - returned: false, + returned: true, } const borrow3: Borrow = { diff --git a/test/routes/book_reservation.test.ts b/test/routes/book_reservation.test.ts index 69d1002..7f3740f 100644 --- a/test/routes/book_reservation.test.ts +++ b/test/routes/book_reservation.test.ts @@ -61,7 +61,7 @@ describe('basic endpoint testing for /bookreservation', () => { test('cancel reservation /bookreservation/cancel', async () => { return request(app) .post('/bookreservation/cancel') - .send({ bookId: 1 }) + .send({ reservationId: 1 }) .set('Authorization', `Bearer 123`) .expect(200) .expect({ ok: true }) @@ -70,7 +70,7 @@ describe('basic endpoint testing for /bookreservation', () => { test('loan reservation /bookreservation/loan', async () => { return request(app) .post('/bookreservation/loan') - .send({ bookId: 3 }) + .send({ reservationId: 3 }) .set('Authorization', `Bearer 123`) .expect(200) .expect({ ok: true }) From 2e6fc0bbf1bdaf7155d7885cff03b8e1306378c0 Mon Sep 17 00:00:00 2001 From: Juho9 Date: Wed, 29 Nov 2023 20:27:33 +0200 Subject: [PATCH 26/26] Fixed formatting? --- src/routes/borrow.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/routes/borrow.ts b/src/routes/borrow.ts index c6f81b5..1661923 100644 --- a/src/routes/borrow.ts +++ b/src/routes/borrow.ts @@ -53,7 +53,9 @@ router.delete('/', async (req: Request, res: Response, next: NextFunction) => { router.post('/', async (req: Request, res: Response, next: NextFunction) => { try { let bookAvailable = await isBookAvailable(req.body.bookId) - let bookReservationStatus = await getCurrentReservationForBook(req.body.bookId) + let bookReservationStatus = await getCurrentReservationForBook( + req.body.bookId + ) if (bookAvailable && bookReservationStatus == null) { let dueDate = new Date() dueDate.setDate(dueDate.getDate() + BORROW_LENGTH)