From 1bc59c76bc0cc209686aacbae7ad04e4a50aa145 Mon Sep 17 00:00:00 2001 From: stefanogoldoni Date: Wed, 12 May 2021 23:39:29 +0200 Subject: [PATCH 1/3] test repository --- .../nest_backend/test/test_zrepository.py | 48 +++++++++++++++---- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/code/backend/nest_backend/test/test_zrepository.py b/code/backend/nest_backend/test/test_zrepository.py index 0df6db0..670e457 100644 --- a/code/backend/nest_backend/test/test_zrepository.py +++ b/code/backend/nest_backend/test/test_zrepository.py @@ -19,15 +19,14 @@ class TestRepositoryGetAll: # assert r.json["data"]["isAdmin"] is True - class TestRepositoryAdd: def test_for_success(self, flask_client: Client, user_headers): r = flask_client.post(f'/api/v1/repositories/', headers=user_headers, json={ 'conditions': [ { - 'content': 'PdS2021', - 'id': 0, - 'type': 0 + 'content': 'PdS2021', + 'id': 0, + 'type': 0 } ], 'evaluation_mode': 0, @@ -37,24 +36,53 @@ class TestRepositoryAdd: assert r.json["result"] == "success" assert r.json["data"]["is_active"] is True - # non vengono passate le condizioni necessarie, in questo caso il nome della repository def test_for_failure(self, flask_client: Client, user_headers): r = flask_client.post(f'/api/v1/repositories/', headers=user_headers, json={ 'conditions': [ { - 'content': 'PdS2021', - 'id': 0, - 'type': 0 + 'content': 'PdS2021', + 'id': 0, + 'type': 0 } ], 'evaluation_mode': 0, - 'is_active': True }) assert r.json["msg"] == "Missing arguments." assert r.json["result"] == "failure" - + + # viene passato un campo evaluation_mode con valore non previsto dall'enum + def test_wrong_evaluation_mode(self, flask_client: Client, user_headers): + r = flask_client.post(f'/api/v1/repositories/', headers=user_headers, json={ + 'conditions': [ + { + 'content': 'PdS2021', + 'id': 0, + 'type': 0 + } + ], + 'evaluation_mode': 99, + 'name': 'repo_test', + 'is_active': True + }) + assert r.json["result"] == "failure" + + # viene passato un campo type con valore non previsto dall'enum + def test_wrong_condition(self, flask_client: Client, user_headers): + r = flask_client.post(f'/api/v1/repositories/', headers=user_headers, json={ + 'conditions': [ + { + 'content': 'PdS2021', + 'id': 0, + 'type': 99 + } + ], + 'evaluation_mode': 2, + 'name': 'repo_test', + 'is_active': True + }) + assert r.json["result"] == "failure" ''' class TestUserDelete: From 6573c5bff577a0956c8c7516f690399e44f00d4d Mon Sep 17 00:00:00 2001 From: stefanogoldoni Date: Thu, 13 May 2021 00:05:00 +0200 Subject: [PATCH 2/3] test repository --- .../nest_backend/test/test_zrepository.py | 49 ++++++++++++------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/code/backend/nest_backend/test/test_zrepository.py b/code/backend/nest_backend/test/test_zrepository.py index 670e457..6da3c40 100644 --- a/code/backend/nest_backend/test/test_zrepository.py +++ b/code/backend/nest_backend/test/test_zrepository.py @@ -3,22 +3,6 @@ from flask.testing import Client '''A file that contains tests of classes and methods for all the requests concerning an user.''' -class TestRepositoryGetAll: - def test_get_all_user_repositories(self, flask_client: Client, user_headers): - r = flask_client.get(f'/api/v1/repositories/', headers=user_headers, - json={'owner_id': 'utente_test@nest.com', 'isActive': False}) - assert r.json["result"] == "success" - # assert r.json["data"]["owner"] == "utente_test@nest.com" - # assert r.json["data"]["isAdmin"] is not True - - def test_get_all_admin_repositories(self, flask_client: Client, admin_headers): - r = flask_client.get(f'/api/v1/repositories/', headers=admin_headers, - json={'owner_id': 'admin@admin.com', 'isActive': False}) - assert r.json["result"] == "success" - # assert r.json["data"]["owner"] == "admin@admin.com" - # assert r.json["data"]["isAdmin"] is True - - class TestRepositoryAdd: def test_for_success(self, flask_client: Client, user_headers): r = flask_client.post(f'/api/v1/repositories/', headers=user_headers, json={ @@ -33,11 +17,12 @@ class TestRepositoryAdd: 'name': 'repo_test', 'is_active': True }) + assert r.status_code == 200 assert r.json["result"] == "success" assert r.json["data"]["is_active"] is True # non vengono passate le condizioni necessarie, in questo caso il nome della repository - def test_for_failure(self, flask_client: Client, user_headers): + def test_no_name(self, flask_client: Client, user_headers): r = flask_client.post(f'/api/v1/repositories/', headers=user_headers, json={ 'conditions': [ { @@ -49,6 +34,7 @@ class TestRepositoryAdd: 'evaluation_mode': 0, 'is_active': True }) + assert r.status_code == 400 assert r.json["msg"] == "Missing arguments." assert r.json["result"] == "failure" @@ -66,6 +52,7 @@ class TestRepositoryAdd: 'name': 'repo_test', 'is_active': True }) + assert r.status_code == 400 assert r.json["result"] == "failure" # viene passato un campo type con valore non previsto dall'enum @@ -82,8 +69,36 @@ class TestRepositoryAdd: 'name': 'repo_test', 'is_active': True }) + assert r.status_code == 400 assert r.json["result"] == "failure" + +class TestRepositoryGetAll: + def test_get_all_user_repositories(self, flask_client: Client, user_headers): + r = flask_client.get(f'/api/v1/repositories/', headers=user_headers, + json={'owner_id': 'utente_test@nest.com', 'isActive': False}) + assert r.status_code == 200 + assert r.json["result"] == "success" + + def test_get_all_admin_repositories(self, flask_client: Client, admin_headers): + r = flask_client.get(f'/api/v1/repositories/', headers=admin_headers, + json={'owner_id': 'admin@admin.com', 'isActive': False}) + assert r.status_code == 200 + assert r.json["result"] == "success" + + +class TestRepositoryGet: + def test_get_existing_repository(self, flask_client: Client, user_headers): + r = flask_client.get(f'/api/v1/repositories/1', headers=user_headers) + assert r.status_code == 200 + assert r.json["result"] == "success" + + def test_get_non_existing_repository(self, flask_client: Client, admin_headers): + r = flask_client.get(f'/api/v1/repositories/99', headers=admin_headers) + assert r.status_code == 404 + assert r.json["result"] == "failure" + + ''' class TestUserDelete: def test_for_success(self, flask_client: Client, admin_headers): From fd92cc161cdefb2dec876a7b01ec4439e1c4657b Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Thu, 13 May 2021 01:07:17 +0200 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=90=9B=20Patch=20some=20more=20bugs,?= =?UTF-8?q?=20introduce=20some=20technical=20debt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- code/frontend/src/App.js | 1 + .../interactive/BoxRepositoriesActive.js | 27 +++++++---- .../interactive/BoxRepositoriesArchived.js | 33 ++++++++----- .../interactive/SummaryRepository.js | 25 ++++------ code/frontend/src/hooks/useBackendViewset.js | 21 +++++++++ code/frontend/src/routes/PageRepositories.js | 46 ++++++++++++------- code/frontend/src/utils/renderContents.js | 1 + 7 files changed, 100 insertions(+), 54 deletions(-) diff --git a/code/frontend/src/App.js b/code/frontend/src/App.js index ae2855a..47f49bd 100644 --- a/code/frontend/src/App.js +++ b/code/frontend/src/App.js @@ -1,3 +1,4 @@ +import React from "react" import Layout from "./components/interactive/Layout" import { BrowserRouter } from "react-router-dom" import GlobalTheme from "./components/providers/GlobalTheme" diff --git a/code/frontend/src/components/interactive/BoxRepositoriesActive.js b/code/frontend/src/components/interactive/BoxRepositoriesActive.js index fa16042..572868c 100644 --- a/code/frontend/src/components/interactive/BoxRepositoriesActive.js +++ b/code/frontend/src/components/interactive/BoxRepositoriesActive.js @@ -3,41 +3,50 @@ import BoxFull from "../base/BoxFull" import SummaryRepository from "./SummaryRepository" import { faFolderOpen } from "@fortawesome/free-solid-svg-icons" import ContextUser from "../../contexts/ContextUser" +import Loading from "../base/Loading" +import BoxFullScrollable from "../base/BoxFullScrollable" /** * A {@link BoxFull} listing all the user's active repositories. * * @param repositories - Array of repositories to display in the box. - * @param refresh - Function that can be called to refresh the repositories list. + * @param archiveRepository - Function to be called when archive is pressed on a repository summary. + * @param destroyRepository - Function to be called when delete is pressed on a repository summary. + * @param running - If an action is currently running. * @param props - Additional props to pass to the box. * @returns {JSX.Element} * @constructor */ -export default function BoxRepositoriesActive({ repositories, refresh, ...props }) { +export default function BoxRepositoriesActive({ repositories, archiveRepository, destroyRepository, running, ...props }) { const { user } = useContext(ContextUser) let contents - if(repositories.length > 0) { + if(repositories === null) { + contents = + } + else if(repositories.length === 0) { + contents = There's nothing here. + } + else { contents = repositories.map(repo => ( archiveRepository(repo["id"])} + deleteSelf={() => destroyRepository(repo["id"])} canArchive={true} canEdit={true} canDelete={repo["owner"]["username"] === user["username"]} + running={running} /> )) } - else { - contents = There's nothing here. - } return ( - + {contents} - + ) } diff --git a/code/frontend/src/components/interactive/BoxRepositoriesArchived.js b/code/frontend/src/components/interactive/BoxRepositoriesArchived.js index 6db7a31..2910c0f 100644 --- a/code/frontend/src/components/interactive/BoxRepositoriesArchived.js +++ b/code/frontend/src/components/interactive/BoxRepositoriesArchived.js @@ -1,43 +1,52 @@ import React, { useContext } from "react" import BoxFull from "../base/BoxFull" -import ContextUser from "../../contexts/ContextUser" import SummaryRepository from "./SummaryRepository" -import { faFolder } from "@fortawesome/free-solid-svg-icons" +import { faFolderOpen } from "@fortawesome/free-solid-svg-icons" +import ContextUser from "../../contexts/ContextUser" +import Loading from "../base/Loading" +import BoxFullScrollable from "../base/BoxFullScrollable" /** * A {@link BoxFull} listing all the user's archived repositories. * * @param repositories - Array of repositories to display in the box. - * @param refresh - Function that can be called to refresh the repositories list. + * @param archiveRepository - Function to be called when archive is pressed on a repository summary. + * @param destroyRepository - Function to be called when delete is pressed on a repository summary. + * @param running - If an action is currently running. * @param props - Additional props to pass to the box. * @returns {JSX.Element} * @constructor */ -export default function BoxRepositoriesArchived({ repositories, refresh, ...props }) { +export default function BoxRepositoriesArchived({ repositories, archiveRepository, destroyRepository, running, ...props }) { const { user } = useContext(ContextUser) let contents - if(repositories.length > 0) { + if(repositories === null) { + contents = + } + else if(repositories.length === 0) { + contents = There's nothing here. + } + else { contents = repositories.map(repo => ( archiveRepository(repo["id"])} + deleteSelf={() => destroyRepository(repo["id"])} canArchive={false} canEdit={false} canDelete={repo["owner"]["username"] === user["username"]} + running={running} /> )) } - else { - contents = There's nothing here. - } return ( - + {contents} - + ) } diff --git a/code/frontend/src/components/interactive/SummaryRepository.js b/code/frontend/src/components/interactive/SummaryRepository.js index 2b3365f..293d1c8 100644 --- a/code/frontend/src/components/interactive/SummaryRepository.js +++ b/code/frontend/src/components/interactive/SummaryRepository.js @@ -13,20 +13,20 @@ import Summary from "../base/Summary" * @param repo - The repository object. * @param refresh - Function that can be called to refresh the repositories list. * @param canDelete - If the Delete button should be displayed or not. + * @param deleteSelf - Function to call when the Delete button is pressed. * @param canEdit - If the Edit button should be displayed or not. * @param canArchive - If the Archive button should be displayed or not. + * @param archiveSelf - Function to call when the Archive button is pressed. + * @param running - If an action is currently running. * @param className - Additional class(es) to be added to the outer box. * @param props - Additional props to pass to the outer box. * @returns {JSX.Element} * @constructor */ export default function SummaryRepository( - { repo, refresh, canDelete, canEdit, canArchive, className, ...props }, + { repo, refresh, canDelete, deleteSelf, canEdit, canArchive, archiveSelf, running, className, ...props }, ) { - const { fetchDataAuth } = useContext(ContextUser) const history = useHistory() - const { fetchNow: archiveThis } = useBackend(fetchDataAuth, "PATCH", `/api/v1/repositories/${repo.id}`, { "close": true }) - const { fetchNow: deletThis } = useBackend(fetchDataAuth, "DELETE", `/api/v1/repositories/${repo.id}`) const onRepoClick = () => { history.push(`/repositories/${repo.id}`) @@ -36,22 +36,13 @@ export default function SummaryRepository( history.push(`/repositories/${repo.id}/edit`) } - const onArchiveClick = async () => { - await archiveThis() - await refresh() - } - - const onDeleteClick = async () => { - await deletThis() - await refresh() - } - const buttons = <> {canDelete ? @@ -61,6 +52,7 @@ export default function SummaryRepository( color={"Yellow"} icon={faPencilAlt} onClick={onEditClick} + disabled={running} > Edit @@ -69,7 +61,8 @@ export default function SummaryRepository( diff --git a/code/frontend/src/hooks/useBackendViewset.js b/code/frontend/src/hooks/useBackendViewset.js index d5c7709..c7843fc 100644 --- a/code/frontend/src/hooks/useBackendViewset.js +++ b/code/frontend/src/hooks/useBackendViewset.js @@ -127,6 +127,25 @@ export default function useBackendViewset(resourcesPath, pkName) { [apiList], ) + const refreshResource = useCallback( + async (pk) => { + try { + const refreshedResource = await apiRetrieve(pk) + setResources(resources => resources.map(resource => { + if(resource[pkName] === pk) { + return refreshedResource + } + return resource + })) + } + catch(e) { + return { error: e } + } + return {} + }, + [apiRetrieve, pkName] + ) + const createResource = useCallback( async (data) => { try { @@ -191,12 +210,14 @@ export default function useBackendViewset(resourcesPath, pkName) { resources, running, loaded, + apiRequest, apiList, apiRetrieve, apiCreate, apiEdit, apiDestroy, refreshResources, + refreshResource, createResource, editResource, destroyResource, diff --git a/code/frontend/src/routes/PageRepositories.js b/code/frontend/src/routes/PageRepositories.js index b706154..995c582 100644 --- a/code/frontend/src/routes/PageRepositories.js +++ b/code/frontend/src/routes/PageRepositories.js @@ -1,32 +1,44 @@ -import React, { useContext } from "react" +import React, { useCallback, useContext } from "react" import Style from "./PageRepositories.module.css" import classNames from "classnames" import BoxRepositoriesActive from "../components/interactive/BoxRepositoriesActive" import BoxRepositoriesArchived from "../components/interactive/BoxRepositoriesArchived" -import useBackendImmediately from "../hooks/useBackendImmediately" -import ContextUser from "../contexts/ContextUser" -import renderContents from "../utils/renderContents" +import useBackendViewset from "../hooks/useBackendViewset" export default function PageRepositories({ children, className, ...props }) { - const { fetchDataAuth } = useContext(ContextUser) - const repositoryRequest = useBackendImmediately(fetchDataAuth, "GET", "/api/v1/repositories/") - const contents = renderContents( - repositoryRequest, - data => { - const repositories = [...data["owner"], ...data["spectator"]] - const active = repositories.filter(r => r.is_active) - const archived = repositories.filter(r => !r.is_active) - return <> - - - + const bv = useBackendViewset("/api/v1/repositories/", "id") + + const archiveRepository = useCallback( + async (pk) => { + try { + await bv.apiRequest("PATCH", `/api/v1/repositories/${pk}`, { + "close": true, + }) + await bv.refreshResource(pk) + } + catch(e) { + return { error: e } + } + return {} }, + [bv.apiRequest, bv.refreshResource] ) return (
- {contents} + r.is_active) : null} + archiveRepository={archiveRepository} + destroyRepository={bv.destroyResource} + running={bv.running} + /> + !r.is_active) : null} + archiveRepository={archiveRepository} + destroyRepository={bv.destroyResource} + running={bv.running} + />
) } diff --git a/code/frontend/src/utils/renderContents.js b/code/frontend/src/utils/renderContents.js index eb3827d..d84965a 100644 --- a/code/frontend/src/utils/renderContents.js +++ b/code/frontend/src/utils/renderContents.js @@ -1,3 +1,4 @@ +import React from "react" import Loading from "../components/base/Loading" import BoxAlert from "../components/base/BoxAlert" import Starting from "../components/base/Starting"