Skip to content

Commit 1fc30c1

Browse files
ChaituVRwa0x6e
andauthored
fix: Allow shutter in proposal privacy if space is using shutter privacy (#475)
* fix: update privacy check logic in proposal * remove unwanted log * fix * test: add tests for proposal privacy * fix tests * Update test/schema.sql * Remove duplicate test * test: remove superfluous assertions * fix handle both undefined and empty string cases in proposal * test: update test for new privacy behavior when missing --------- Co-authored-by: Wan Qi Chen <[email protected]>
1 parent 640b7e5 commit 1fc30c1

File tree

4 files changed

+140
-18
lines changed

4 files changed

+140
-18
lines changed

src/ingestor.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ export default async function ingestor(req) {
142142
body: message.body,
143143
discussion: message.discussion || '',
144144
choices: message.choices,
145-
privacy: message.privacy || '',
145+
privacy: message.privacy,
146146
labels: message.labels || [],
147147
start: message.start,
148148
end: message.end,
@@ -169,7 +169,7 @@ export default async function ingestor(req) {
169169
name: message.title,
170170
body: message.body,
171171
discussion: message.discussion || '',
172-
privacy: message.privacy || '',
172+
privacy: message.privacy,
173173
choices: message.choices,
174174
labels: message.labels || [],
175175
metadata: {

src/writer/proposal.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,10 @@ export async function verify(body): Promise<any> {
102102
if (msg.payload.type !== space.voting.type) return Promise.reject('invalid voting type');
103103
}
104104

105-
if (space.voting?.privacy !== 'any' && msg.payload.privacy) {
105+
const spacePrivacy = space.voting?.privacy ?? 'any';
106+
const proposalPrivacy = msg.payload.privacy;
107+
108+
if (proposalPrivacy !== undefined && spacePrivacy !== 'any' && spacePrivacy !== proposalPrivacy) {
106109
return Promise.reject('not allowed to set privacy');
107110
}
108111

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

218221
let quorum = spaceSettings.voting?.quorum || 0;

src/writer/update-proposal.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,10 @@ export async function verify(body): Promise<any> {
4949

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

52-
if (space.voting?.privacy !== 'any' && msg.payload.privacy) {
52+
const spacePrivacy = space.voting?.privacy ?? 'any';
53+
const proposalPrivacy = msg.payload.privacy;
54+
55+
if (proposalPrivacy !== undefined && spacePrivacy !== 'any' && spacePrivacy !== proposalPrivacy) {
5356
return Promise.reject('not allowed to set privacy');
5457
}
5558

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

7679
const proposal = {

test/unit/writer/proposal.test.ts

Lines changed: 126 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
import omit from 'lodash/omit';
2-
import * as writer from '../../../src/writer/proposal';
3-
import input from '../../fixtures/writer-payload/proposal.json';
4-
import { spacesGetSpaceFixtures } from '../../fixtures/space';
52
import {
3+
ACTIVE_PROPOSAL_BY_AUTHOR_LIMIT,
64
ECOSYSTEM_SPACE_PROPOSAL_DAY_LIMIT,
7-
FLAGGED_SPACE_PROPOSAL_DAY_LIMIT,
8-
SPACE_PROPOSAL_DAY_LIMIT,
9-
VERIFIED_SPACE_PROPOSAL_DAY_LIMIT,
105
ECOSYSTEM_SPACE_PROPOSAL_MONTH_LIMIT,
6+
FLAGGED_SPACE_PROPOSAL_DAY_LIMIT,
117
FLAGGED_SPACE_PROPOSAL_MONTH_LIMIT,
12-
SPACE_PROPOSAL_MONTH_LIMIT,
13-
VERIFIED_SPACE_PROPOSAL_MONTH_LIMIT,
148
MAINNET_ECOSYSTEM_SPACES,
15-
ACTIVE_PROPOSAL_BY_AUTHOR_LIMIT,
9+
SPACE_PROPOSAL_DAY_LIMIT,
10+
SPACE_PROPOSAL_MONTH_LIMIT,
1611
TURBO_SPACE_PROPOSAL_DAY_LIMIT,
17-
TURBO_SPACE_PROPOSAL_MONTH_LIMIT
12+
TURBO_SPACE_PROPOSAL_MONTH_LIMIT,
13+
VERIFIED_SPACE_PROPOSAL_DAY_LIMIT,
14+
VERIFIED_SPACE_PROPOSAL_MONTH_LIMIT
1815
} from '../../../src/helpers/limits';
16+
import * as writer from '../../../src/writer/proposal';
17+
import { spacesGetSpaceFixtures } from '../../fixtures/space';
18+
import input from '../../fixtures/writer-payload/proposal.json';
1919

2020
const FLAGGED_ADDRESSES = ['0x0'];
2121

@@ -69,6 +69,12 @@ mockGetProposalsCount.mockResolvedValue([
6969
}
7070
]);
7171

72+
function updateInputPayload(input: any, payload: any) {
73+
const msg = JSON.parse(input.msg);
74+
75+
return { ...input, msg: JSON.stringify({ ...msg, payload: { ...msg.payload, ...payload } }) };
76+
}
77+
7278
describe('writer/proposal', () => {
7379
afterEach(jest.clearAllMocks);
7480

@@ -294,6 +300,116 @@ describe('writer/proposal', () => {
294300
expect(mockSnapshotUtilsValidate).toHaveBeenCalledTimes(0);
295301
});
296302
});
303+
304+
describe('when the space is using ANY privacy', () => {
305+
beforeEach(() => {
306+
mockGetSpace.mockResolvedValueOnce({
307+
...spacesGetSpaceFixtures,
308+
voting: { privacy: 'any' }
309+
});
310+
});
311+
312+
it('accepts a proposal with shutter privacy', () => {
313+
return expect(
314+
writer.verify(updateInputPayload(input, { privacy: 'shutter' }))
315+
).resolves.toBeUndefined();
316+
});
317+
318+
it('accepts a proposal with undefined privacy', () => {
319+
return expect(
320+
writer.verify(updateInputPayload(input, { privacy: undefined }))
321+
).resolves.toBeUndefined();
322+
});
323+
324+
it('accepts a proposal with no privacy', () => {
325+
return expect(
326+
writer.verify(updateInputPayload(input, { privacy: '' }))
327+
).resolves.toBeUndefined();
328+
});
329+
});
330+
331+
// Fallback as if { privacy: 'any' }
332+
describe('when the space is missing the privacy settings', () => {
333+
beforeEach(() => {
334+
mockGetSpace.mockResolvedValueOnce({
335+
...spacesGetSpaceFixtures,
336+
voting: undefined
337+
});
338+
});
339+
340+
it('accepts a proposal with shutter privacy', () => {
341+
return expect(
342+
writer.verify(updateInputPayload(input, { privacy: 'shutter' }))
343+
).resolves.toBeUndefined();
344+
});
345+
346+
it('accepts a proposal with undefined privacy', () => {
347+
return expect(
348+
writer.verify(updateInputPayload(input, { privacy: undefined }))
349+
).resolves.toBeUndefined();
350+
});
351+
352+
it('accepts a proposal with no privacy', () => {
353+
return expect(
354+
writer.verify(updateInputPayload(input, { privacy: '' }))
355+
).resolves.toBeUndefined();
356+
});
357+
});
358+
359+
describe('when the space is using NO privacy', () => {
360+
beforeEach(() => {
361+
mockGetSpace.mockResolvedValueOnce({
362+
...spacesGetSpaceFixtures,
363+
voting: { privacy: '' }
364+
});
365+
});
366+
367+
it('rejects a proposal with shutter privacy', () => {
368+
return expect(
369+
writer.verify(updateInputPayload(input, { privacy: 'shutter' }))
370+
).rejects.toMatch('not allowed to set privacy');
371+
});
372+
373+
it('accepts a proposal with undefined privacy', () => {
374+
return expect(
375+
writer.verify(updateInputPayload(input, { privacy: undefined }))
376+
).resolves.toBeUndefined();
377+
});
378+
379+
it('accepts a proposal with no privacy', () => {
380+
return expect(
381+
writer.verify(updateInputPayload(input, { privacy: '' }))
382+
).resolves.toBeUndefined();
383+
});
384+
});
385+
386+
describe('when the space is using SHUTTER privacy', () => {
387+
beforeEach(() => {
388+
mockGetSpace.mockResolvedValueOnce({
389+
...spacesGetSpaceFixtures,
390+
voting: { privacy: 'shutter' }
391+
});
392+
});
393+
394+
it('accepts a proposal with shutter privacy', () => {
395+
return expect(
396+
writer.verify(updateInputPayload(input, { privacy: 'shutter' }))
397+
).resolves.toBeUndefined();
398+
});
399+
400+
it('accepts a proposal with undefined privacy', () => {
401+
return expect(
402+
writer.verify(updateInputPayload(input, { privacy: undefined }))
403+
).resolves.toBeUndefined();
404+
});
405+
406+
it('rejects a proposal with privacy empty string', async () => {
407+
expect.assertions(1);
408+
await expect(writer.verify(updateInputPayload(input, { privacy: '' }))).rejects.toMatch(
409+
'not allowed to set privacy'
410+
);
411+
});
412+
});
297413
});
298414

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

0 commit comments

Comments
 (0)