Skip to content

Commit

Permalink
Merge pull request #15295 from davelopez/add_storage_api
Browse files Browse the repository at this point in the history
Add Storage Management API
  • Loading branch information
jmchilton authored Mar 20, 2023
2 parents 0a34ae2 + 46a3b80 commit e831a7d
Show file tree
Hide file tree
Showing 23 changed files with 1,231 additions and 334 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,39 @@ import { mount } from "@vue/test-utils";
import flushPromises from "flush-promises";
import { getLocalVue } from "@tests/jest/helpers";
import CleanupOperationSummary from "./CleanupOperationSummary.vue";
import { CleanableSummary, type CleanupOperation, CleanupResult } from "./model";
import { CleanableSummary, type CleanupOperation, CleanupResult, type CleanableItem } from "./model";

const localVue = getLocalVue();

const REVIEW_ITEMS_LINK = '[data-test-id="review-link"]';
const NO_ITEMS_INDICATOR = '[data-test-id="no-items-indicator"]';

const EXPECTED_ITEMS: CleanableItem[] = [
{ id: "1", name: "Item 1", size: 512, type: "dataset", update_time: new Date().toISOString() },
{ id: "2", name: "Item 2", size: 512, type: "dataset", update_time: new Date().toISOString() },
];

/** Operation that can clean some items */
const CLEANUP_OPERATION: CleanupOperation = {
id: "operation-id",
name: "operation name",
description: "operation description",
fetchSummary: async () =>
new CleanableSummary({
totalSize: 1024,
totalItems: 2,
total_size: 1024,
total_items: 2,
}),
fetchItems: async () => [],
cleanupItems: async () =>
new CleanupResult({
totalItemCount: 2,
totalFreeBytes: 1024,
errors: [],
}),
new CleanupResult(
{
total_item_count: 2,
success_item_count: 2,
total_free_bytes: 1024,
errors: [],
},
EXPECTED_ITEMS
),
};
/** Operation without items to clean*/
const EMPTY_CLEANUP_OPERATION: CleanupOperation = {
Expand All @@ -34,8 +43,8 @@ const EMPTY_CLEANUP_OPERATION: CleanupOperation = {
description: "operation that has no items to clean",
fetchSummary: async () =>
new CleanableSummary({
totalSize: 0,
totalItems: 0,
total_size: 0,
total_items: 0,
}),
fetchItems: async () => [],
cleanupItems: async () => new CleanupResult(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { mount } from "@vue/test-utils";
import flushPromises from "flush-promises";
import { getLocalVue } from "tests/jest/helpers";
import CleanupResultDialog from "./CleanupResultDialog.vue";
import { CleanupResult } from "./model";
import { CleanupResult, type CleanableItem } from "./model";

const localVue = getLocalVue();

Expand All @@ -14,25 +14,41 @@ const ERRORS_TABLE = '[data-test-id="errors-table"]';

const NO_RESULT_YET = undefined;
const FAILED_RESULT = () => {
return new CleanupResult({
errorMessage: "The operation failed",
totalFreeBytes: 0,
totalItemCount: 0,
errors: [],
});
return new CleanupResult(
{
total_item_count: 0,
errors: [],
success_item_count: 0,
total_free_bytes: 0,
},
[],
"The operation failed"
);
};
const TEST_ITEMS: CleanableItem[] = [
{ id: "1", name: "Dataset X", size: 512, type: "dataset", update_time: new Date().toISOString() },
{ id: "2", name: "Dataset Y", size: 512, type: "dataset", update_time: new Date().toISOString() },
{ id: "3", name: "Dataset Z", size: 512, type: "dataset", update_time: new Date().toISOString() },
];
const PARTIAL_SUCCESS_RESULT = () => {
return new CleanupResult({
totalItemCount: 3,
totalFreeBytes: 1,
errors: [
{ name: "Dataset X", reason: "Failed because of X" },
{ name: "Dataset Y", reason: "Failed because of Y" },
],
});
return new CleanupResult(
{
total_item_count: 3,
success_item_count: 1,
total_free_bytes: 512,
errors: [
{ item_id: "1", error: "Failed because of X" },
{ item_id: "2", error: "Failed because of Y" },
],
},
TEST_ITEMS
);
};
const SUCCESS_RESULT = () => {
return new CleanupResult({ totalItemCount: 2, totalFreeBytes: 2, errors: [] });
return new CleanupResult(
{ total_item_count: 3, success_item_count: 3, total_free_bytes: 512 * 3, errors: [] },
TEST_ITEMS
);
};
async function mountCleanupResultDialogWith(result?: CleanupResult) {
const wrapper = mount(CleanupResultDialog, { propsData: { result, show: true }, localVue });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import localize from "@/utils/localization";
import { computed, ref } from "vue";
import type { CleanupResult } from "./model";
import Alert from "components/Alert.vue";
interface CleanupResultDialogProps {
result?: CleanupResult;
Expand Down Expand Up @@ -46,6 +47,9 @@ defineExpose({
<template>
<b-modal id="cleanup-result-modal" v-model="showModal" :title="title" title-tag="h2" hide-footer static>
<div class="text-center">
<Alert
variant="info"
message="After the operation, the storage space that will be freed up will only be for the unique items. This means that some items may not free up any storage space because they are duplicates of other items." />
<b-spinner v-if="isLoading" class="mx-auto" data-test-id="loading-spinner" />
<div v-else>
<b-alert v-if="result.hasFailed" show variant="danger" data-test-id="error-alert">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { mount, type WrapperArray } from "@vue/test-utils";
import { mount, type Wrapper, type WrapperArray } from "@vue/test-utils";
import flushPromises from "flush-promises";
import { getLocalVue } from "tests/jest/helpers";
import { CleanableSummary, type CleanupOperation, CleanupResult } from "./model";
import { CleanableSummary, type CleanupOperation, CleanupResult, type CleanableItem } from "./model";
import ReviewCleanupDialog from "./ReviewCleanupDialog.vue";

const localVue = getLocalVue();
Expand All @@ -12,26 +12,31 @@ const SELECT_ALL_CHECKBOX = '[data-test-id="select-all-checkbox"]';
const AGREEMENT_CHECKBOX = '[data-test-id="agreement-checkbox"]';
const CONFIRMATION_MODAL = "#confirmation-modal";

const EXPECTED_TOTAL_ITEMS = 2;
const EXPECTED_ITEMS: CleanableItem[] = [
{ id: "1", name: "Item 1", size: 512, type: "dataset", update_time: new Date().toISOString() },
{ id: "2", name: "Item 2", size: 512, type: "dataset", update_time: new Date().toISOString() },
];
const EXPECTED_TOTAL_ITEMS = EXPECTED_ITEMS.length;
const FAKE_OPERATION: CleanupOperation = {
id: "operation-id",
name: "operation name",
description: "operation description",
fetchSummary: async () =>
new CleanableSummary({
totalSize: 1024,
totalItems: EXPECTED_TOTAL_ITEMS,
total_size: 1024,
total_items: EXPECTED_TOTAL_ITEMS,
}),
fetchItems: async () => [
{ id: "1", name: "Item 1", size: 512, update_time: new Date().toISOString(), hda_ldda: "hda" },
{ id: "2", name: "Item 2", size: 512, update_time: new Date().toISOString(), hda_ldda: "hda" },
],
fetchItems: async () => EXPECTED_ITEMS,
cleanupItems: async () =>
new CleanupResult({
totalItemCount: EXPECTED_TOTAL_ITEMS,
totalFreeBytes: 1024,
errors: [],
}),
new CleanupResult(
{
total_item_count: EXPECTED_TOTAL_ITEMS,
success_item_count: EXPECTED_TOTAL_ITEMS,
total_free_bytes: 1024,
errors: [],
},
EXPECTED_ITEMS
),
};

async function mountReviewCleanupDialogWith(operation: CleanupOperation, totalItems = EXPECTED_TOTAL_ITEMS) {
Expand All @@ -43,6 +48,11 @@ async function mountReviewCleanupDialogWith(operation: CleanupOperation, totalIt
return wrapper;
}

async function setAllItemsChecked(wrapper: Wrapper<Vue>) {
await wrapper.find(SELECT_ALL_CHECKBOX).setChecked();
await flushPromises();
}

describe("ReviewCleanupDialog.vue", () => {
it("should display a table with items to review", async () => {
const wrapper = await mountReviewCleanupDialogWith(FAKE_OPERATION);
Expand All @@ -54,20 +64,19 @@ describe("ReviewCleanupDialog.vue", () => {
it("should disable the delete button if no items are selected", async () => {
const wrapper = await mountReviewCleanupDialogWith(FAKE_OPERATION);
const deleteButton = wrapper.find(DELETE_BUTTON);
const selectAllCheckbox = wrapper.find(SELECT_ALL_CHECKBOX);

expect(deleteButton.attributes().disabled).toBeTruthy();
// TODO: explicit any because the type of the vm is not correctly inferred, remove when fixed
expect((wrapper.vm as any).selectedItems.length).toBe(0);
await selectAllCheckbox.setChecked();
await setAllItemsChecked(wrapper);
// TODO: explicit any because the type of the vm is not correctly inferred, remove when fixed
expect((wrapper.vm as any).selectedItems.length).toBe(EXPECTED_TOTAL_ITEMS);
expect(deleteButton.attributes().disabled).toBeFalsy();
});

it("should show a confirmation message when deleting items", async () => {
const wrapper = await mountReviewCleanupDialogWith(FAKE_OPERATION);
await wrapper.find(SELECT_ALL_CHECKBOX).setChecked();
await setAllItemsChecked(wrapper);
const confirmationModal = wrapper.find(CONFIRMATION_MODAL);

expect(confirmationModal.attributes("aria-hidden")).toBeTruthy();
Expand All @@ -77,7 +86,7 @@ describe("ReviewCleanupDialog.vue", () => {

it("should disable the confirmation button until the agreement has been accepted", async () => {
const wrapper = await mountReviewCleanupDialogWith(FAKE_OPERATION);
await wrapper.find(SELECT_ALL_CHECKBOX).setChecked();
await setAllItemsChecked(wrapper);
await wrapper.find(DELETE_BUTTON).trigger("click");
const allButtons = wrapper.findAll(".btn");
const permanentlyDeleteBtn = withNameFilter(allButtons).hasText("Permanently delete").at(0);
Expand All @@ -89,7 +98,7 @@ describe("ReviewCleanupDialog.vue", () => {

it("should emit the confirmation event when the agreement and deletion has been confirmed", async () => {
const wrapper = await mountReviewCleanupDialogWith(FAKE_OPERATION);
await wrapper.find(SELECT_ALL_CHECKBOX).setChecked();
await setAllItemsChecked(wrapper);
await wrapper.find(DELETE_BUTTON).trigger("click");
await wrapper.find(AGREEMENT_CHECKBOX).setChecked();
const allButtons = wrapper.findAll(".btn");
Expand Down
Loading

0 comments on commit e831a7d

Please sign in to comment.