Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,40 @@ class AllocationsFrontendIntegrationTest
},
)

// TODO (#4915): test withdraw and reject like in the test below
val allocationRequestElement = clue("find the allocation request element") {
eventually() {
findAll(className("allocation-request")).toSeq.loneElement
}
}

actAndCheck(
"click on withdrawing the allocation", {
val allocationElement = findAll(className("allocation")).toSeq.loneElement
click on allocationElement
.findChildElement(className("allocation-withdraw"))
.valueOrFail("Could not find withdraw button for allocation")
},
)(
"the allocation is not shown anymore",
_ => {
findAll(className("allocation")).toSeq shouldBe empty withClue "Allocation Cards"
},
)

actAndCheck(
"click on rejecting the allocation request", {
click on allocationRequestElement
.findChildElement(className("allocation-request-reject"))
.valueOrFail("Could not find reject button for allocation request")
},
)(
"the allocation request is not shown anymore",
_ => {
findAll(
className("allocation-request")
).toSeq shouldBe empty withClue "Allocation Request Cards"
},
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ package org.lfdecentralizedtrust.splice.integration.tests
import com.digitalasset.canton.data.CantonTimestamp
import com.digitalasset.canton.{HasActorSystem, HasExecutionContext}
import org.lfdecentralizedtrust.splice.codegen.java.splice.api.token.allocationv1.*
import org.lfdecentralizedtrust.splice.codegen.java.splice.api.token.{allocationv2, metadatav1}
import org.lfdecentralizedtrust.splice.codegen.java.splice.api.token.holdingv1.InstrumentId
import org.lfdecentralizedtrust.splice.codegen.java.splice.api.token.holdingv2.{
Account as AccountV2,
InstrumentId as InstrumentIdV2,
}
import org.lfdecentralizedtrust.splice.codegen.java.splice.api.token.transferinstructionv1.TransferInstruction
import org.lfdecentralizedtrust.splice.http.v0.definitions.TransferInstructionResultOutput.members
import org.lfdecentralizedtrust.splice.integration.EnvironmentDefinition
Expand Down Expand Up @@ -127,14 +132,66 @@ class TokenStandardFetchFallbackIntegrationTest
},
)

// TODO (#4915): check that the fallback also works for V2
clue("SV-1's Scan sees it (still, even though ingestion is paused)") {
clue("SV-1's Scan sees V1 allocation (still, even though ingestion is paused)") {
eventuallySucceeds() {
sv1ScanBackend.getAllocationTransferContext(
allocation.contract.contractId.toInterface(Allocation.INTERFACE)
)
}
}

val referenceIdV2 = UUID.randomUUID().toString

val (_, allocationV2) = actAndCheck(
"Alice creates a V2 Allocation",
aliceWalletClient.allocateAmulet(
new allocationv2.AllocationSpecification(
new allocationv2.SettlementInfo(
java.util.List.of(dsoParty.toProtoPrimitive),
new allocationv2.Reference(referenceIdV2, Optional.empty),
Instant.now,
Instant.now.plusSeconds(3600L),
Optional.of(Instant.now.plusSeconds(2 * 3600L)),
new metadatav1.Metadata(java.util.Map.of()),
),
java.util.List.of(
new allocationv2.TransferLeg(
UUID.randomUUID().toString,
new AccountV2(aliceParty.toProtoPrimitive, Optional.empty(), ""),
new AccountV2(bobParty.toProtoPrimitive, Optional.empty(), ""),
BigDecimal(10).bigDecimal,
new InstrumentIdV2(dsoParty.toProtoPrimitive, "Amulet"),
new metadatav1.Metadata(java.util.Map.of()),
)
),
new AccountV2(aliceParty.toProtoPrimitive, Optional.empty(), ""),
)
),
)(
"Alice sees the V2 Allocation",
_ => {
val alloc = inside(aliceWalletClient.listAmuletAllocations()) {
case _ :+ (v2Alloc: HttpWalletAppClient.TokenStandard.V2AmuletAllocation) =>
v2Alloc
}
alloc.contract.payload.allocation.settlement.settlementRef.id should be(
referenceIdV2
)
alloc
},
)

clue(
"SV-1's Scan sees V2 allocation cancel context (still, even though ingestion is paused)"
) {
eventuallySucceeds() {
sv1ScanBackend.getAllocationV2CancelContext(
allocationV2.contract.contractId.toInterface(
allocationv2.Allocation.INTERFACE
)
)
}
}
}
}

Expand Down
190 changes: 181 additions & 9 deletions apps/wallet/frontend/src/__tests__/wallet.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
AllocateAmuletResponse as AllocateAmuletV1Response,
AllocateAmuletV2Request,
AllocateAmuletV2Response,
AmuletAllocationV2WithdrawResult,
AmuletAllocationWithdrawResult,
ChoiceExecutionMetadata,
ListAllocationRequestsResponse,
Expand All @@ -53,6 +54,7 @@ import { AnyContract } from '@daml.js/splice-api-token-metadata/lib/Splice/Api/T
import { AmuletAllocationV2 } from '@daml.js/splice-amulet/lib/Splice/AmuletAllocationV2';
import { AmuletAllocation as AmuletAllocationV1 } from '@daml.js/splice-amulet/lib/Splice/AmuletAllocation';
import { Contract } from '@lfdecentralizedtrust/splice-common-frontend-utils';
import { LockedAmulet } from '@daml.js/splice-amulet/lib/Splice/Amulet';

const dsoEntry = nameServiceEntries.find(e => e.name.startsWith('dso'))!;

Expand Down Expand Up @@ -232,7 +234,7 @@ describe('Wallet user can', () => {
describe('Allocations', () => {
test('see allocations', async () => {
const allocations = Array.from({ length: 3 }, (_, i) =>
getAllocation(
getAllocationV2(
`settlement_${i}`,
`transfer_leg_${i}`,
`receiver_${i}::party`,
Expand Down Expand Up @@ -438,14 +440,13 @@ describe('Wallet user can', () => {
expect(calledWithBody).toStrictEqual(expected);
});

// TODO (#4915): implement this test for v2
test("withdraw allocations v1 from the allocation or the allocation request's leg views", async () => {
const allocationRequestPayload = getAllocationRequestV1();
const allocationRequest = mkContract(AllocationRequestV1, allocationRequestPayload);
const allocationRequests = [allocationRequest];
const allocation = mkContract(
AmuletAllocationV1,
getAllocation(
getAllocationV1(
allocationRequestPayload.settlement.settlementRef.id,
'acceptable',
allocationRequestPayload.transferLegs.acceptable.receiver,
Expand Down Expand Up @@ -508,16 +509,93 @@ describe('Wallet user can', () => {
expect(await screen.findByLabelText(`Allocations ${allocations.length}`)).toBeDefined();

const withdrawButtons = await screen.findAllByRole('button', { name: 'Withdraw' });
expect(withdrawButtons).to.have.length(1);
expect(withdrawButtons).to.have.length(2);

for (const button of withdrawButtons) {
await user.click(button);
}

expect(calledWithdrawArgs).toStrictEqual([allocation.contract_id]);
expect(calledWithdrawArgs).toStrictEqual([allocation.contract_id, allocation.contract_id]);
});

test("withdraw allocations v2 from the allocation or the allocation request's views", async () => {
const allocationRequestPayload = getAllocationRequestV2();
const allocationRequest = mkContract(AllocationRequestV2, allocationRequestPayload);
const allocationRequests = [allocationRequest];
const allocation = mkContract(
AmuletAllocationV2,
getAllocationV2(
allocationRequestPayload.settlement.settlementRef.id,
'acceptable',
bobPartyId,
'3',
allocationRequestPayload.settlement.executors[0]
)
);
const allocations = [allocation];

const calledWithdrawArgs: string[] = [];

server.use(
rest.get(
`${walletUrl}/v0/wallet/token-standard/allocation-requests`,
(_req, res, ctx) => {
return res(
ctx.json<ListAllocationRequestsResponse>({
allocation_requests: allocationRequests.map(contract => {
return { contract };
}),
})
);
}
),
rest.get(`${walletUrl}/v0/allocations`, (_req, res, ctx) => {
return res(
ctx.json<ListAllocationsResponse>({
allocations: allocations.map(contract => {
return { contract };
}),
})
);
}),
rest.post(`${walletUrl}/v2/allocations/:cid/withdraw`, (req, res, ctx) => {
calledWithdrawArgs.push(req.params.cid.toString());
return res(
ctx.json<AmuletAllocationV2WithdrawResult>({
authorizer_holding_cids: {},
meta: {},
})
);
})
);

const user = userEvent.setup();
render(
<WalletConfigProvider>
<App />
</WalletConfigProvider>
);
expect(await screen.findByText('Allocations')).toBeDefined();
const allocationsLink = screen.getByRole('link', { name: 'Allocations' });
await user.click(allocationsLink);

// there should be one allocation request and one allocation,
// both of which with a withdraw button
expect(
await screen.findByLabelText(`Allocation Requests ${allocationRequests.length}`)
).toBeDefined();
expect(await screen.findByLabelText(`Allocations ${allocations.length}`)).toBeDefined();

const withdrawButtons = await screen.findAllByRole('button', { name: 'Withdraw' });
expect(withdrawButtons).to.have.length(2);

for (const button of withdrawButtons) {
await user.click(button);
}

expect(calledWithdrawArgs).toStrictEqual([allocation.contract_id, allocation.contract_id]);
});

// TODO (#4915): implement this test for v2
test('reject allocation requests', async () => {
const allocationRequestPayload = getAllocationRequestV1();
const allocationRequest = mkContract(AllocationRequestV1, allocationRequestPayload);
Expand Down Expand Up @@ -578,6 +656,67 @@ describe('Wallet user can', () => {

expect(calledRejectArgs).toStrictEqual([allocationRequest.contract_id]);
});

test('reject allocation requests v2', async () => {
const allocationRequestPayload = getAllocationRequestV2();
const allocationRequest = mkContract(AllocationRequestV2, allocationRequestPayload);
const allocationRequests = [allocationRequest];

const calledRejectArgs: string[] = [];

server.use(
rest.get(
`${walletUrl}/v0/wallet/token-standard/allocation-requests`,
(_req, res, ctx) => {
return res(
ctx.json<ListAllocationRequestsResponse>({
allocation_requests: allocationRequests.map(contract => {
return { contract };
}),
})
);
}
),
rest.get(`${walletUrl}/v0/allocations`, (_req, res, ctx) => {
return res(
ctx.json<ListAllocationsResponse>({
allocations: [],
})
);
}),
rest.post(
`${walletUrl}/v0/wallet/token-standard/allocation-requests/:cid/reject`,
(req, res, ctx) => {
calledRejectArgs.push(req.params.cid.toString());
return res(
ctx.json<ChoiceExecutionMetadata>({
meta: {},
})
);
}
)
);

const user = userEvent.setup();
render(
<WalletConfigProvider>
<App />
</WalletConfigProvider>
);
expect(await screen.findByText('Allocations')).toBeDefined();
const allocationsLink = screen.getByRole('link', { name: 'Allocations' });
await user.click(allocationsLink);

// there should be one allocation request with a reject button
expect(
await screen.findByLabelText(`Allocation Requests ${allocationRequests.length}`)
).toBeDefined();

const rejectButton = await screen.findByRole('button', { name: 'Reject' });
await user.click(rejectButton);

expect(calledRejectArgs).toStrictEqual([allocationRequest.contract_id]);
});
});
});

Expand Down Expand Up @@ -1365,15 +1504,15 @@ function getAllocationRequestV2() {
};
}

function getAllocation(
function getAllocationV2(
settlementId: string,
transferLegId: string,
receiver: string,
amount: string,
executor: string
) {
): AmuletAllocationV2 {
return {
lockedAmulet: null as damlTypes.Optional<string>,
lockedAmulet: null as damlTypes.Optional<ContractId<LockedAmulet>>,
dso: dsoPartyId,
expiresAt: new Date().toISOString(),
allocation: {
Expand Down Expand Up @@ -1402,3 +1541,36 @@ function getAllocation(
},
};
}

function getAllocationV1(
settlementId: string,
transferLegId: string,
receiver: string,
amount: string,
executor: string
): AmuletAllocationV1 {
return {
lockedAmulet: `lockedamulet${settlementId}` as ContractId<LockedAmulet>,
allocation: {
transferLegId,
transferLeg: {
sender: alicePartyId,
receiver,
amount,
meta: { values: {} },
instrumentId: { id: 'Amulet', admin: 'dso::party' },
},
settlement: {
executor,
settlementRef: {
id: settlementId,
cid: null,
},
requestedAt: new Date().toISOString(),
allocateBefore: new Date().toISOString(),
settleBefore: new Date().toISOString(),
meta: { values: {} },
},
},
};
}
Loading
Loading