Skip to content

Commit

Permalink
[wip]: Add more setData JS tests
Browse files Browse the repository at this point in the history
  • Loading branch information
lorisleiva committed Jan 16, 2025
1 parent 321b286 commit 9ca8c99
Show file tree
Hide file tree
Showing 5 changed files with 314 additions and 17 deletions.
11 changes: 8 additions & 3 deletions clients/js/src/generated/instructions/setData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
getStructEncoder,
getU8Decoder,
getU8Encoder,
none,
transformEncoder,
type Address,
type Codec,
Expand Down Expand Up @@ -107,7 +108,7 @@ export type SetDataInstructionDataArgs = {
compression: CompressionArgs;
format: FormatArgs;
dataSource: DataSourceArgs;
data: OptionOrNullable<ReadonlyUint8Array>;
data?: OptionOrNullable<ReadonlyUint8Array>;
};

export function getSetDataInstructionDataEncoder(): Encoder<SetDataInstructionDataArgs> {
Expand All @@ -120,7 +121,11 @@ export function getSetDataInstructionDataEncoder(): Encoder<SetDataInstructionDa
['dataSource', getDataSourceEncoder()],
['data', getOptionEncoder(getBytesEncoder(), { prefix: null })],
]),
(value) => ({ ...value, discriminator: SET_DATA_DISCRIMINATOR })
(value) => ({
...value,
discriminator: SET_DATA_DISCRIMINATOR,
data: value.data ?? none(),
})
);
}

Expand Down Expand Up @@ -166,7 +171,7 @@ export type SetDataInput<
compression: SetDataInstructionDataArgs['compression'];
format: SetDataInstructionDataArgs['format'];
dataSource: SetDataInstructionDataArgs['dataSource'];
data: SetDataInstructionDataArgs['data'];
data?: SetDataInstructionDataArgs['data'];
};

export function getSetDataInstruction<
Expand Down
13 changes: 13 additions & 0 deletions clients/js/test/extend.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import test from 'ava';

test.todo(
'the program authority of a canonical metadata account can extend it'
);

test.todo(
'the explicit authority of a canonical metadata account can extend it'
);

test.todo(
'the metadata authority of a non-canonical metadata account can extend it'
);
297 changes: 283 additions & 14 deletions clients/js/test/setData.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { getTransferSolInstruction } from '@solana-program/system';
import {
address,
appendTransactionMessageInstruction,
appendTransactionMessageInstructions,
generateKeyPairSigner,
getUtf8Encoder,
isSolanaError,
pipe,
SOLANA_ERROR__INSTRUCTION_ERROR__INVALID_ACCOUNT_DATA,
} from '@solana/web3.js';
import test from 'ava';
import {
Expand All @@ -14,18 +18,19 @@ import {
Format,
getSetAuthorityInstruction,
getSetDataInstruction,
getSetImmutableInstruction,
Metadata,
} from '../src';
import {
createCanonicalMetadata,
createDefaultSolanaClient,
createDefaultTransaction,
createDeployedProgram,
createKeypairBuffer,
createNonCanonicalMetadata,
generateKeyPairSignerWithSol,
signAndSendTransaction,
} from './_setup';
import { getTransferSolInstruction } from '@solana-program/system';

test('the program authority of a canonical metadata account can update its data using instruction data', async (t) => {
// Given the following authority and deployed program.
Expand Down Expand Up @@ -224,22 +229,286 @@ test('the authority of a non-canonical metadata account can update its data usin
});
});

test.todo(
'the program authority of a canonical metadata account can update its data using a pre-allocated buffer'
);
test('the program authority of a canonical metadata account can update its data using a pre-allocated buffer', async (t) => {
// Given the following authority and deployed program.
const client = createDefaultSolanaClient();
const authority = await generateKeyPairSignerWithSol(client);
const [program, programData] = await createDeployedProgram(client, authority);

test.todo(
'the explicit authority of a canonical metadata account can update its data using a pre-allocated buffer'
);
// And the following initialized canonical metadata account.
const originalData = getUtf8Encoder().encode('Original data');
const [metadata] = await createCanonicalMetadata(client, {
authority,
program,
programData,
seed: 'dummy',
encoding: Encoding.None,
compression: Compression.None,
format: Format.None,
dataSource: DataSource.Direct,
data: originalData,
});

test.todo(
'the authority of a non-canonical metadata account can update its data using a pre-allocated buffer'
);
// And the following pre-allocated buffer account with written data.
const newData = getUtf8Encoder().encode('https://example.com/new-data.json');
const buffer = await createKeypairBuffer(client, {
payer: authority,
data: newData,
});

// When the program authority updates the data of the metadata account using the buffer.
const setDataIx = getSetDataInstruction({
metadata,
authority,
buffer: buffer.address,
program,
programData,
encoding: Encoding.Utf8,
compression: Compression.Gzip,
format: Format.Json,
dataSource: DataSource.Url,
});
await pipe(
await createDefaultTransaction(client, authority),
(tx) => appendTransactionMessageInstruction(setDataIx, tx),
(tx) => signAndSendTransaction(client, tx)
);

// Then we expect the metadata account have the new data.
const account = await fetchMetadata(client.rpc, metadata);
t.like(account.data, <Metadata>{
encoding: Encoding.Utf8,
compression: Compression.Gzip,
format: Format.Json,
dataSource: DataSource.Url,
data: newData,
});
});

test('the explicit authority of a canonical metadata account can update its data using a pre-allocated buffer', async (t) => {
// Given the following authorities and deployed program.
const client = createDefaultSolanaClient();
const [authority, explicitAuthority] = await Promise.all([
generateKeyPairSignerWithSol(client),
generateKeyPairSigner(),
]);
const [program, programData] = await createDeployedProgram(client, authority);

// And the following initialized canonical metadata account.
const originalData = getUtf8Encoder().encode('Original data');
const [metadata] = await createCanonicalMetadata(client, {
authority,
program,
programData,
seed: 'dummy',
encoding: Encoding.None,
compression: Compression.None,
format: Format.None,
dataSource: DataSource.Direct,
data: originalData,
});

// And the following pre-allocated buffer account with written data.
const newData = getUtf8Encoder().encode('https://example.com/new-data.json');
const buffer = await createKeypairBuffer(client, {
payer: authority,
data: newData,
});

// And given an explicit authority is set on the metadata account.
const setAuthorityIx = getSetAuthorityInstruction({
metadata,
authority,
program,
programData,
newAuthority: explicitAuthority.address,
});

// When the explicit authority updates the data of the metadata account using the buffer.
const setDataIx = getSetDataInstruction({
metadata,
authority: explicitAuthority,
buffer: buffer.address,
program,
programData,
encoding: Encoding.Utf8,
compression: Compression.Gzip,
format: Format.Json,
dataSource: DataSource.Url,
});
await pipe(
await createDefaultTransaction(client, authority),
(tx) =>
appendTransactionMessageInstructions([setAuthorityIx, setDataIx], tx),
(tx) => signAndSendTransaction(client, tx)
);

// Then we expect the metadata account have the new data.
const account = await fetchMetadata(client.rpc, metadata);
t.like(account.data, <Metadata>{
encoding: Encoding.Utf8,
compression: Compression.Gzip,
format: Format.Json,
dataSource: DataSource.Url,
data: newData,
});
});

test('the authority of a non-canonical metadata account can update its data using a pre-allocated buffer', async (t) => {
// Given the following authority and deployed program.
const client = createDefaultSolanaClient();
const authority = await generateKeyPairSignerWithSol(client);
const program = address('TokenKEGQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA');

// And the following initialized non-canonical metadata account.
const originalData = getUtf8Encoder().encode('Original data');
const [metadata] = await createNonCanonicalMetadata(client, {
authority,
program,
seed: 'dummy',
encoding: Encoding.None,
compression: Compression.None,
format: Format.None,
dataSource: DataSource.Direct,
data: originalData,
});

// And the following pre-allocated buffer account with written data.
const newData = getUtf8Encoder().encode('https://example.com/new-data.json');
const buffer = await createKeypairBuffer(client, {
payer: authority,
data: newData,
});

// When the metadata authority updates the account using the pre-allocated buffer.
const setDataIx = getSetDataInstruction({
metadata,
authority,
buffer: buffer.address,
program,
encoding: Encoding.Utf8,
compression: Compression.Gzip,
format: Format.Json,
dataSource: DataSource.Url,
});
await pipe(
await createDefaultTransaction(client, authority),
(tx) => appendTransactionMessageInstruction(setDataIx, tx),
(tx) => signAndSendTransaction(client, tx)
);

// Then we expect the metadata account have the new data.
const account = await fetchMetadata(client.rpc, metadata);
t.like(account.data, <Metadata>{
encoding: Encoding.Utf8,
compression: Compression.Gzip,
format: Format.Json,
dataSource: DataSource.Url,
data: newData,
});
});

test('an immutable canonical metadata account cannot be updated', async (t) => {
// Given the following authority and deployed program.
const client = createDefaultSolanaClient();
const authority = await generateKeyPairSignerWithSol(client);
const [program, programData] = await createDeployedProgram(client, authority);

// And the following initialized canonical metadata account.
const [metadata] = await createCanonicalMetadata(client, {
authority,
program,
programData,
seed: 'dummy',
data: getUtf8Encoder().encode('Original data'),
});

// And given the metadata account is immutable.
const setImmutableIx = getSetImmutableInstruction({
metadata,
authority,
program,
programData,
});

// When the program authority tries to update the data of the metadata account.
const setDataIx = getSetDataInstruction({
metadata,
authority,
program,
programData,
encoding: Encoding.Utf8,
compression: Compression.None,
format: Format.Json,
dataSource: DataSource.Direct,
data: getUtf8Encoder().encode('New data'),
});
const promise = pipe(
await createDefaultTransaction(client, authority),
(tx) =>
appendTransactionMessageInstructions([setImmutableIx, setDataIx], tx),
(tx) => signAndSendTransaction(client, tx)
);

// Then we expect the transaction to fail.
const error = await t.throwsAsync(promise);
t.true(
isSolanaError(
error.cause,
SOLANA_ERROR__INSTRUCTION_ERROR__INVALID_ACCOUNT_DATA
)
);
});

test('an immutable non-canonical metadata account cannot be updated', async (t) => {
// Given the following authority and deployed program.
const client = createDefaultSolanaClient();
const authority = await generateKeyPairSignerWithSol(client);
const program = address('TokenKEGQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA');

// And the following initialized non-canonical metadata account.
const [metadata] = await createNonCanonicalMetadata(client, {
authority,
program,
seed: 'dummy',
data: getUtf8Encoder().encode('Original data'),
});

// And given the metadata account is immutable.
const setImmutableIx = getSetImmutableInstruction({
metadata,
authority,
program,
});

// When the metadata authority tries to update the data.
const setDataIx = getSetDataInstruction({
metadata,
authority,
program,
encoding: Encoding.Utf8,
compression: Compression.None,
format: Format.Json,
dataSource: DataSource.Direct,
data: getUtf8Encoder().encode('New data'),
});
const promise = pipe(
await createDefaultTransaction(client, authority),
(tx) =>
appendTransactionMessageInstructions([setImmutableIx, setDataIx], tx),
(tx) => signAndSendTransaction(client, tx)
);

// Then we expect the transaction to fail.
const error = await t.throwsAsync(promise);
t.true(
isSolanaError(
error.cause,
SOLANA_ERROR__INSTRUCTION_ERROR__INVALID_ACCOUNT_DATA
)
);
});

test.todo(
'The metadata account needs to be extended for data changes that add more than 1KB'
);

test.todo('an immutable canonical metadata account cannot be updated');

test.todo('an immutable non-canonical metadata account cannot be updated');
9 changes: 9 additions & 0 deletions clients/js/test/trim.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import test from 'ava';

test.todo('the program authority of a canonical metadata account can trim it');

test.todo('the explicit authority of a canonical metadata account can trim it');

test.todo(
'the metadata authority of a non-canonical metadata account can trim it'
);
Loading

0 comments on commit 9ca8c99

Please sign in to comment.