Skip to content

Commit

Permalink
Merge branch 'master' into feat-whitelist-custom-domain-for-walletcon…
Browse files Browse the repository at this point in the history
…nect
  • Loading branch information
wa0x6e authored Jan 11, 2025
2 parents 9deb920 + bf2b8de commit ca3282a
Show file tree
Hide file tree
Showing 8 changed files with 241 additions and 26 deletions.
4 changes: 2 additions & 2 deletions src/ingestor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ export default async function ingestor(req) {
body: message.body,
discussion: message.discussion || '',
choices: message.choices,
privacy: message.privacy || '',
privacy: message.privacy,
labels: message.labels || [],
start: message.start,
end: message.end,
Expand All @@ -169,7 +169,7 @@ export default async function ingestor(req) {
name: message.title,
body: message.body,
discussion: message.discussion || '',
privacy: message.privacy || '',
privacy: message.privacy,
choices: message.choices,
labels: message.labels || [],
metadata: {
Expand Down
7 changes: 6 additions & 1 deletion src/writer/follow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ import db from '../helpers/mysql';
import { DEFAULT_NETWORK_ID, NETWORK_IDS } from '../helpers/utils';

export const getFollowsCount = async (follower: string): Promise<number> => {
const query = `SELECT COUNT(*) AS count FROM follows WHERE follower = ?`;
const query = `
SELECT COUNT(*) AS count
FROM follows
JOIN spaces ON spaces.id = follows.space
WHERE follower = ? AND spaces.deleted = 0
`;

const [{ count }] = await db.queryAsync(query, [follower]);

Expand Down
9 changes: 6 additions & 3 deletions src/writer/proposal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,10 @@ export async function verify(body): Promise<any> {
if (msg.payload.type !== space.voting.type) return Promise.reject('invalid voting type');
}

if (space.voting?.privacy !== 'any' && msg.payload.privacy) {
const spacePrivacy = space.voting?.privacy ?? 'any';
const proposalPrivacy = msg.payload.privacy;

if (proposalPrivacy !== undefined && spacePrivacy !== 'any' && spacePrivacy !== proposalPrivacy) {
return Promise.reject('not allowed to set privacy');
}

Expand Down Expand Up @@ -210,9 +213,9 @@ export async function action(body, ipfs, receipt, id): Promise<void> {
const plugins = JSON.stringify(metadata.plugins || {});
const spaceNetwork = spaceSettings.network;
const proposalSnapshot = parseInt(msg.payload.snapshot || '0');
let privacy = spaceSettings.voting?.privacy || '';
let privacy = spaceSettings.voting?.privacy ?? 'any';
if (privacy === 'any') {
privacy = msg.payload.privacy;
privacy = msg.payload.privacy ?? '';
}

let quorum = spaceSettings.voting?.quorum || 0;
Expand Down
4 changes: 4 additions & 0 deletions src/writer/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ export async function verify(body): Promise<any> {
const isAdmin = admins.includes(body.address.toLowerCase());
const newAdmins = (msg.payload.admins || []).map(admin => admin.toLowerCase());

if (msg.payload.domain && !space?.turbo && !space?.domain) {
return Promise.reject('domain is a turbo feature only');
}

const anotherSpaceWithDomain = (
await db.queryAsync('SELECT 1 FROM spaces WHERE domain = ? AND id != ? LIMIT 1', [
msg.payload.domain,
Expand Down
9 changes: 6 additions & 3 deletions src/writer/update-proposal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ export async function verify(body): Promise<any> {

if (proposal.author !== body.address) return Promise.reject('Not the author');

if (space.voting?.privacy !== 'any' && msg.payload.privacy) {
const spacePrivacy = space.voting?.privacy ?? 'any';
const proposalPrivacy = msg.payload.privacy;

if (proposalPrivacy !== undefined && spacePrivacy !== 'any' && spacePrivacy !== proposalPrivacy) {
return Promise.reject('not allowed to set privacy');
}

Expand All @@ -68,9 +71,9 @@ export async function action(body, ipfs): Promise<void> {
const metadata = msg.payload.metadata || {};
const plugins = JSON.stringify(metadata.plugins || {});
const spaceSettings = await getSpace(msg.space);
let privacy = spaceSettings.voting?.privacy || '';
let privacy = spaceSettings.voting?.privacy ?? 'any';
if (privacy === 'any') {
privacy = msg.payload.privacy;
privacy = msg.payload.privacy ?? '';
}

const proposal = {
Expand Down
32 changes: 26 additions & 6 deletions test/integration/writer/follows.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { verify, action } from '../../../src/writer/follow';
import { FOLLOWS_LIMIT_PER_USER } from '../../../src/helpers/limits';
import db, { sequencerDB } from '../../../src/helpers/mysql';
import { action, verify } from '../../../src/writer/follow';
import { spacesSqlFixtures } from '../../fixtures/space';

describe('writer/follow', () => {
Expand All @@ -9,7 +9,7 @@ describe('writer/follow', () => {

afterAll(async () => {
await db.queryAsync('DELETE FROM follows');
await db.queryAsync('DELETE FROM spaces WHERE id = ?', [`${TEST_PREFIX}-${space.id}`]);
await db.queryAsync('DELETE FROM spaces WHERE id LIKE ?', [`${TEST_PREFIX}%`]);
await db.endAsync();
await sequencerDB.endAsync();
});
Expand All @@ -18,14 +18,22 @@ describe('writer/follow', () => {
const followerId = '0x0';

beforeAll(async () => {
let i = 0;
let i = 1;
const promises: Promise<any>[] = [];

while (i <= FOLLOWS_LIMIT_PER_USER) {
promises.push(
db.queryAsync('INSERT INTO snapshot_sequencer_test.spaces SET ?', {
...space,
id: `${TEST_PREFIX}${i}.eth`,
deleted: 0,
settings: JSON.stringify(space.settings)
})
);
promises.push(
db.queryAsync(
'INSERT INTO follows SET id = ?, ipfs = ?, follower = ?, space = ?, created = ?',
[i, i, followerId, `test-${i}.eth`, i]
[i, i, followerId, `${TEST_PREFIX}${i}.eth`, i]
)
);

Expand All @@ -41,6 +49,18 @@ describe('writer/follow', () => {
);
});

it('ignores deleted spaces from the limit', async () => {
await db.queryAsync('UPDATE snapshot_sequencer_test.spaces SET deleted = 1 WHERE id = ?', [
`${TEST_PREFIX}1.eth`
]);

await expect(verify({ from: followerId })).resolves.toEqual(true);

return db.queryAsync('UPDATE snapshot_sequencer_test.spaces SET deleted = 0 WHERE id = ?', [
`${TEST_PREFIX}1.eth`
]);
});

it('returns true when the user has not reached the limit', () => {
return expect(verify({ from: '0x1' })).resolves.toEqual(true);
});
Expand Down Expand Up @@ -111,15 +131,15 @@ describe('writer/follow', () => {
it('should increment the follower count of the space', async () => {
await db.queryAsync('INSERT INTO spaces SET ?', {
...space,
id: `${TEST_PREFIX}-${space.id}`,
id: `${TEST_PREFIX}${space.id}`,
settings: JSON.stringify(space.settings)
});

const id = '3';
const ipfs = '4';
const message = {
from: '0x4',
space: `${TEST_PREFIX}-${space.id}`,
space: `${TEST_PREFIX}${space.id}`,
timestamp: 1
};

Expand Down
136 changes: 126 additions & 10 deletions test/unit/writer/proposal.test.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import omit from 'lodash/omit';
import * as writer from '../../../src/writer/proposal';
import input from '../../fixtures/writer-payload/proposal.json';
import { spacesGetSpaceFixtures } from '../../fixtures/space';
import {
ACTIVE_PROPOSAL_BY_AUTHOR_LIMIT,
ECOSYSTEM_SPACE_PROPOSAL_DAY_LIMIT,
FLAGGED_SPACE_PROPOSAL_DAY_LIMIT,
SPACE_PROPOSAL_DAY_LIMIT,
VERIFIED_SPACE_PROPOSAL_DAY_LIMIT,
ECOSYSTEM_SPACE_PROPOSAL_MONTH_LIMIT,
FLAGGED_SPACE_PROPOSAL_DAY_LIMIT,
FLAGGED_SPACE_PROPOSAL_MONTH_LIMIT,
SPACE_PROPOSAL_MONTH_LIMIT,
VERIFIED_SPACE_PROPOSAL_MONTH_LIMIT,
MAINNET_ECOSYSTEM_SPACES,
ACTIVE_PROPOSAL_BY_AUTHOR_LIMIT,
SPACE_PROPOSAL_DAY_LIMIT,
SPACE_PROPOSAL_MONTH_LIMIT,
TURBO_SPACE_PROPOSAL_DAY_LIMIT,
TURBO_SPACE_PROPOSAL_MONTH_LIMIT
TURBO_SPACE_PROPOSAL_MONTH_LIMIT,
VERIFIED_SPACE_PROPOSAL_DAY_LIMIT,
VERIFIED_SPACE_PROPOSAL_MONTH_LIMIT
} from '../../../src/helpers/limits';
import * as writer from '../../../src/writer/proposal';
import { spacesGetSpaceFixtures } from '../../fixtures/space';
import input from '../../fixtures/writer-payload/proposal.json';

const FLAGGED_ADDRESSES = ['0x0'];

Expand Down Expand Up @@ -69,6 +69,12 @@ mockGetProposalsCount.mockResolvedValue([
}
]);

function updateInputPayload(input: any, payload: any) {
const msg = JSON.parse(input.msg);

return { ...input, msg: JSON.stringify({ ...msg, payload: { ...msg.payload, ...payload } }) };
}

describe('writer/proposal', () => {
afterEach(jest.clearAllMocks);

Expand Down Expand Up @@ -294,6 +300,116 @@ describe('writer/proposal', () => {
expect(mockSnapshotUtilsValidate).toHaveBeenCalledTimes(0);
});
});

describe('when the space is using ANY privacy', () => {
beforeEach(() => {
mockGetSpace.mockResolvedValueOnce({
...spacesGetSpaceFixtures,
voting: { privacy: 'any' }
});
});

it('accepts a proposal with shutter privacy', () => {
return expect(
writer.verify(updateInputPayload(input, { privacy: 'shutter' }))
).resolves.toBeUndefined();
});

it('accepts a proposal with undefined privacy', () => {
return expect(
writer.verify(updateInputPayload(input, { privacy: undefined }))
).resolves.toBeUndefined();
});

it('accepts a proposal with no privacy', () => {
return expect(
writer.verify(updateInputPayload(input, { privacy: '' }))
).resolves.toBeUndefined();
});
});

// Fallback as if { privacy: 'any' }
describe('when the space is missing the privacy settings', () => {
beforeEach(() => {
mockGetSpace.mockResolvedValueOnce({
...spacesGetSpaceFixtures,
voting: undefined
});
});

it('accepts a proposal with shutter privacy', () => {
return expect(
writer.verify(updateInputPayload(input, { privacy: 'shutter' }))
).resolves.toBeUndefined();
});

it('accepts a proposal with undefined privacy', () => {
return expect(
writer.verify(updateInputPayload(input, { privacy: undefined }))
).resolves.toBeUndefined();
});

it('accepts a proposal with no privacy', () => {
return expect(
writer.verify(updateInputPayload(input, { privacy: '' }))
).resolves.toBeUndefined();
});
});

describe('when the space is using NO privacy', () => {
beforeEach(() => {
mockGetSpace.mockResolvedValueOnce({
...spacesGetSpaceFixtures,
voting: { privacy: '' }
});
});

it('rejects a proposal with shutter privacy', () => {
return expect(
writer.verify(updateInputPayload(input, { privacy: 'shutter' }))
).rejects.toMatch('not allowed to set privacy');
});

it('accepts a proposal with undefined privacy', () => {
return expect(
writer.verify(updateInputPayload(input, { privacy: undefined }))
).resolves.toBeUndefined();
});

it('accepts a proposal with no privacy', () => {
return expect(
writer.verify(updateInputPayload(input, { privacy: '' }))
).resolves.toBeUndefined();
});
});

describe('when the space is using SHUTTER privacy', () => {
beforeEach(() => {
mockGetSpace.mockResolvedValueOnce({
...spacesGetSpaceFixtures,
voting: { privacy: 'shutter' }
});
});

it('accepts a proposal with shutter privacy', () => {
return expect(
writer.verify(updateInputPayload(input, { privacy: 'shutter' }))
).resolves.toBeUndefined();
});

it('accepts a proposal with undefined privacy', () => {
return expect(
writer.verify(updateInputPayload(input, { privacy: undefined }))
).resolves.toBeUndefined();
});

it('rejects a proposal with privacy empty string', async () => {
expect.assertions(1);
await expect(writer.verify(updateInputPayload(input, { privacy: '' }))).rejects.toMatch(
'not allowed to set privacy'
);
});
});
});

it('rejects if the snapshot is in the future', async () => {
Expand Down
Loading

0 comments on commit ca3282a

Please sign in to comment.