/* * MIT License * * Copyright (c) 2020-present Cloudogu GmbH and Contributors * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * */ import { setIndexLink } from "./tests/indexLinks"; import createInfiniteCachingClient from "./tests/createInfiniteCachingClient"; import { Namespace, Permission, PermissionCollection, Repository, RepositoryRole, RepositoryRoleCollection, RepositoryVerbs } from "@scm-manager/ui-types"; import fetchMock from "fetch-mock-jest"; import { renderHook } from "@testing-library/react-hooks"; import createWrapper from "./tests/createWrapper"; import { useAvailablePermissions, useCreatePermission, useDeletePermission, usePermissions, useRepositoryVerbs, useUpdatePermission } from "./permissions"; import { act } from "react-test-renderer"; describe("permission hooks test", () => { const readRole: RepositoryRole = { name: "READ", verbs: ["read", "pull"], _links: {} }; const roleCollection: RepositoryRoleCollection = { _embedded: { repositoryRoles: [readRole] }, _links: {}, page: 1, pageTotal: 1 }; const verbCollection: RepositoryVerbs = { verbs: ["read", "pull"], _links: {} }; const readPermission: Permission = { name: "trillian", role: "READ", verbs: [], groupPermission: false, _links: { update: { href: "/p/trillian" } } }; const writePermission: Permission = { name: "dent", role: "WRITE", verbs: [], groupPermission: false, _links: { delete: { href: "/p/dent" } } }; const permissionsRead: PermissionCollection = { _embedded: { permissions: [readPermission] }, _links: {} }; const permissionsWrite: PermissionCollection = { _embedded: { permissions: [writePermission] }, _links: {} }; const namespace: Namespace = { namespace: "spaceships", _links: { permissions: { href: "/ns/spaceships/permissions" } } }; const repository: Repository = { namespace: "spaceships", name: "heart-of-gold", type: "git", _links: { permissions: { href: "/r/heart-of-gold/permissions" } } }; const queryClient = createInfiniteCachingClient(); beforeEach(() => { queryClient.clear(); fetchMock.reset(); }); describe("useRepositoryVerbs tests", () => { it("should return available verbs", async () => { setIndexLink(queryClient, "repositoryVerbs", "/verbs"); fetchMock.get("/api/v2/verbs", verbCollection); const { result, waitFor } = renderHook(() => useRepositoryVerbs(), { wrapper: createWrapper(undefined, queryClient) }); await waitFor(() => { return !!result.current.data; }); expect(result.current.data).toEqual(verbCollection); }); }); describe("useAvailablePermissions tests", () => { it("should return available roles and verbs", async () => { queryClient.setQueryData("index", { version: "x.y.z", _links: { repositoryRoles: { href: "/roles" }, repositoryVerbs: { href: "/verbs" } } }); fetchMock.get("/api/v2/roles", roleCollection); fetchMock.get("/api/v2/verbs", verbCollection); const { result, waitFor } = renderHook(() => useAvailablePermissions(), { wrapper: createWrapper(undefined, queryClient) }); await waitFor(() => { return !!result.current.data; }); expect(result.current.data?.repositoryRoles).toEqual(roleCollection._embedded.repositoryRoles); expect(result.current.data?.repositoryVerbs).toEqual(verbCollection.verbs); }); }); describe("usePermissions tests", () => { const fetchPermissions = async (namespaceOrRepository: Namespace | Repository) => { const { result, waitFor } = renderHook(() => usePermissions(namespaceOrRepository), { wrapper: createWrapper(undefined, queryClient) }); await waitFor(() => { return !!result.current.data; }); return result.current.data; }; it("should return permissions from namespace", async () => { fetchMock.getOnce("/api/v2/ns/spaceships/permissions", permissionsRead); const data = await fetchPermissions(namespace); expect(data).toEqual(permissionsRead); }); it("should cache permissions for namespace", async () => { fetchMock.getOnce("/api/v2/ns/spaceships/permissions", permissionsRead); await fetchPermissions(namespace); const data = queryClient.getQueryData(["namespace", "spaceships", "permissions"]); expect(data).toEqual(permissionsRead); }); it("should return permissions from repository", async () => { fetchMock.getOnce("/api/v2/r/heart-of-gold/permissions", permissionsWrite); const data = await fetchPermissions(repository); expect(data).toEqual(permissionsWrite); }); it("should cache permissions for repository", async () => { fetchMock.getOnce("/api/v2/r/heart-of-gold/permissions", permissionsWrite); await fetchPermissions(repository); const data = queryClient.getQueryData(["repository", "spaceships", "heart-of-gold", "permissions"]); expect(data).toEqual(permissionsWrite); }); }); describe("useCreatePermission tests", () => { const createAndFetch = async () => { fetchMock.postOnce("/api/v2/ns/spaceships/permissions", { status: 201, headers: { Location: "/ns/spaceships/permissions/42" } }); fetchMock.getOnce("/api/v2/ns/spaceships/permissions/42", readPermission); const { result, waitForNextUpdate } = renderHook(() => useCreatePermission(namespace), { wrapper: createWrapper(undefined, queryClient) }); await act(() => { const { create } = result.current; create(readPermission); return waitForNextUpdate(); }); return result.current; }; it("should create permission", async () => { const data = await createAndFetch(); expect(data.permission).toEqual(readPermission); }); it("should fail without location header", async () => { fetchMock.postOnce("/api/v2/ns/spaceships/permissions", { status: 201 }); const { result, waitForNextUpdate } = renderHook(() => useCreatePermission(namespace), { wrapper: createWrapper(undefined, queryClient) }); await act(() => { const { create } = result.current; create(readPermission); return waitForNextUpdate(); }); expect(result.current.error).toBeDefined(); }); it("should invalidate namespace cache", async () => { const key = ["namespace", "spaceships", "permissions"]; queryClient.setQueryData(key, permissionsRead); await createAndFetch(); const state = queryClient.getQueryState(key); expect(state?.isInvalidated).toBe(true); }); }); describe("useDeletePermission tests", () => { const deletePermission = async () => { fetchMock.deleteOnce("/api/v2/p/dent", { status: 204 }); const { result, waitForNextUpdate } = renderHook(() => useDeletePermission(repository), { wrapper: createWrapper(undefined, queryClient) }); await act(() => { const { remove } = result.current; remove(writePermission); return waitForNextUpdate(); }); return result.current; }; const shouldInvalidateQuery = async (queryKey: string[], data: unknown) => { queryClient.setQueryData(queryKey, data); await deletePermission(); const queryState = queryClient.getQueryState(queryKey); expect(queryState?.isInvalidated).toBe(true); }; it("should delete permission", async () => { const { isDeleted } = await deletePermission(); expect(isDeleted).toBe(true); }); it("should invalidate permission cache", async () => { await shouldInvalidateQuery(["repository", "spaceships", "heart-of-gold", "permissions"], permissionsWrite); }); }); describe("useUpdatePermission tests", () => { const updatePermission = async () => { fetchMock.putOnce("/api/v2/p/trillian", { status: 204 }); const { result, waitForNextUpdate } = renderHook(() => useUpdatePermission(repository), { wrapper: createWrapper(undefined, queryClient) }); await act(() => { const { update } = result.current; update(readPermission); return waitForNextUpdate(); }); return result.current; }; const shouldInvalidateQuery = async (queryKey: string[], data: unknown) => { queryClient.setQueryData(queryKey, data); await updatePermission(); const queryState = queryClient.getQueryState(queryKey); expect(queryState?.isInvalidated).toBe(true); }; it("should update permission", async () => { const { isUpdated } = await updatePermission(); expect(isUpdated).toBe(true); }); it("should invalidate permission cache", async () => { await shouldInvalidateQuery(["repository", "spaceships", "heart-of-gold", "permissions"], permissionsRead); }); }); });