git ssb

0+

Piet / ssb-loomio



Commit 39e44cdf4226b11684fd63254ddb23629d327b78

Merge pull request #22 from ssbc/results-own-domain

Move into results folder.
Piet Geursen authored on 4/11/2018, 2:40:26 AM
GitHub committed on 4/11/2018, 2:40:26 AM
Parent: 80c90c52e4088319b7d48db43edeb53d3bba8ffe
Parent: ebad2e906b7c5bfaaf2ef70e09eb2ed8b39f698d

Files changed

poll/async/get.jschanged
position/sync/buildResults.jsdeleted
test/position/sync/buildResults.test.jsdeleted
test/results/sync/buildResults.test.jsadded
results/sync/buildResults.jsadded
poll/async/get.jsView
@@ -3,9 +3,9 @@
33 const { isPoll, isPosition, isChooseOnePoll, isChooseOnePosition } = require('ssb-poll-schema')
44 isPoll.chooseOne = isChooseOnePoll
55 isPosition.chooseOne = isChooseOnePosition
66 const { ERROR_POSITION_TYPE } = require('../../types')
7-const getResults = require('../../position/sync/buildResults')
7+const getResults = require('../../results/sync/buildResults')
88 const getMsgContent = require('../../lib/getMsgContent')
99
1010 module.exports = function (server) {
1111 return function get (key, cb) {
position/sync/buildResults.jsView
@@ -1,72 +1,0 @@
1-const getMsgContent = require('../../lib/getMsgContent')
2-const PositionChoiceError = require('../../errors/sync/positionChoiceError')
3-const PositionLateError = require('../../errors/sync/positionLateError')
4-const { isChooseOnePoll } = require('ssb-poll-schema')
5-
6-// Expects `poll` and `position` objects passed in to be of shape:
7-// {
8-// key,
9-// value: {
10-// content: {...},
11-// timestamp: ...
12-// author: ...
13-// }
14-// }
15-//
16-// postions must be of the correct type ie: type checked by the caller.
17-module.exports = function ({positions, poll}) {
18- if (isChooseOnePoll(poll)) {
19- return chooseOneResults({positions, poll})
20- }
21-}
22-
23-function chooseOneResults ({positions, poll}) {
24- var results = getMsgContent(poll)
25- .details
26- .choices
27- .map(choice => {
28- return {
29- choice,
30- voters: {}
31- }
32- })
33-
34- return positions.reduce(function (acc, position) {
35- const { author, content } = position.value
36- const { choice } = content.details
37-
38- if (isInvalidChoice({position, poll})) {
39- acc.errors.push(PositionChoiceError({position}))
40- return acc
41- }
42-
43- if (isPositionLate({position, poll})) {
44- acc.errors.push(PositionLateError({position}))
45- return acc
46- }
47-
48- deleteExistingVotesByAuthor({results: acc.results, author})
49- acc.results[choice].voters[author] = position
50-
51- return acc
52- }, {errors: [], results})
53-}
54-
55-// !!! assumes these are already sorted by time.
56-// modifies results passed in
57-function deleteExistingVotesByAuthor ({author, results}) {
58- results.forEach(result => {
59- if (result.voters[author]) {
60- delete result.voters[author]
61- }
62- })
63-}
64-
65-function isInvalidChoice ({position, poll}) {
66- const { choice } = position.value.content.details
67- return choice >= poll.value.content.details.choices.length
68-}
69-
70-function isPositionLate ({position, poll}) {
71- return position.value.timestamp > poll.value.content.closesAt
72-}
test/position/sync/buildResults.test.jsView
@@ -1,210 +1,0 @@
1-const test = require('tape')
2-const pull = require('pull-stream')
3-const ChooseOne = require('../../../position/async/buildChooseOne')()
4-const ChooseOnePoll = require('../../../poll/sync/buildChooseOne')
5-const chooseOneResults = require('../../../position/sync/buildResults')
6-const {isPosition, isPoll} = require('ssb-poll-schema')
7-const {ERROR_POSITION_CHOICE, ERROR_POSITION_LATE} = require('../../../types')
8-
9-const pietId = '@Mq8D3YC6VdErKQzV3oi2oK5hHSoIwR0hUQr4M46wr/0=.ed25519'
10-const mixId = '@Mq8D3YC6VdErKQzV3oi2oK5hHSoIwR0hUQr4M46wr/1=.ed25519'
11-const mikeyId = '@Mq8D3YC6VdErKQzV3oi2oK5hHSoIwR0hUQr4M46wr/2=.ed25519'
12-const timmyId = '@Mq8D3YC6VdErKQzV3oi2oK5hHSoIwR0hUQr4M46wr/3=.ed25519'
13-const tommyId = '@Mq8D3YC6VdErKQzV3oi2oK5hHSoIwR0hUQr4M46wr/4=.ed25519'
14-const sallyId = '@Mq8D3YC6VdErKQzV3oi2oK5hHSoIwR0hUQr4M46wr/5=.ed25519'
15-
16-const poll = '%t+PhrNxxXkw/jMo6mnwUWfFjJapoPWxzsQoe0Np+nYw=.sha256'
17-
18-const now = new Date().toISOString()
19-
20-const validPoll = {
21- key: '%t+PhrNxxXkw/jMo6mnwUWfFjJapoPWxzsQoe0Np+nYw=.sha256',
22- value: {
23- content: ChooseOnePoll({
24- choices: [1, 2, 'three'],
25- title: 'how many food',
26- closesAt: now
27- })
28- }
29-}
30-
31-test('ChooseOneResults - ChooseOneResults', function (t) {
32- const positions = [
33- { value: { content: {choice: 0, poll}, author: pietId } },
34- { value: { content: {choice: 0, poll}, author: mixId } },
35- { value: { content: {choice: 0, poll}, author: mikeyId } },
36- { value: { content: {choice: 1, poll}, author: timmyId } },
37- { value: { content: {choice: 1, poll}, author: tommyId } },
38- { value: { content: {choice: 2, poll}, author: sallyId } }
39- ]
40-
41- const expected = {
42- results: [
43- {
44- choice: 1,
45- voters: {
46- [pietId]: positions[0],
47- [mixId]: positions[1],
48- [mikeyId]: positions[2]
49- }
50- },
51- {
52- choice: 2,
53- voters: {
54- [timmyId]: positions[3],
55- [tommyId]: positions[4]
56- }
57- },
58- {
59- choice: 'three',
60- voters: {
61- [sallyId]: positions[5]
62- }
63- }
64- ],
65- errors: {}
66- }
67-
68- pull(
69- pull.values(positions),
70- pull.asyncMap((fullPosition, cb) => {
71- ChooseOne(fullPosition.value.content, (err, position) => {
72- fullPosition.value.content = position
73- cb(err, fullPosition)
74- })
75- }),
76- pull.collect(postions => {
77- const actual = chooseOneResults({positions, poll: validPoll})
78- t.deepEqual(actual, expected, 'results are correct')
79- t.end()
80- })
81- )
82-})
83-
84-test('ChooseOneResults - a position stated for an invalid choice index is not counted', function (t) {
85- const positions = [
86- { value: { content: {choice: 3, poll}, author: pietId } }
87- ]
88-
89- pull(
90- pull.values(positions),
91- pull.asyncMap((fullPosition, cb) => {
92- ChooseOne(fullPosition.value.content, (err, position) => {
93- fullPosition.value.content = position
94- cb(err, fullPosition)
95- })
96- }),
97- pull.collect(postions => {
98- const actual = chooseOneResults({positions, poll: validPoll})
99- t.false(actual.results[3], 'invalid vote is not counted')
100- t.end()
101- })
102- )
103-})
104-
105-test('ChooseOneResults - a position stated for an invalid choice index is included in the errors object', function (t) {
106- const positions = [
107- { value: { content: {choice: 3, poll}, author: pietId } }
108- ]
109-
110- pull(
111- pull.values(positions),
112- pull.asyncMap((fullPosition, cb) => {
113- ChooseOne(fullPosition.value.content, (err, position) => {
114- fullPosition.value.content = position
115- cb(err, fullPosition)
116- })
117- }),
118- pull.collect(postions => {
119- const actual = chooseOneResults({positions, poll: validPoll})
120- t.deepEqual(actual.errors[0].type, ERROR_POSITION_CHOICE, 'invalid vote is on error object')
121- t.end()
122- })
123- )
124-})
125-
126-test('ChooseOneResults - A position stated before the closing time of the poll is counted', function (t) {
127- const positions = [
128- { value: { content: {choice: 0, poll}, author: pietId, timestamp: now - 1 } }
129- ]
130-
131- pull(
132- pull.values(positions),
133- pull.asyncMap((fullPosition, cb) => {
134- ChooseOne(fullPosition.value.content, (err, position) => {
135- fullPosition.value.content = position
136- cb(err, fullPosition)
137- })
138- }),
139- pull.collect(postions => {
140- const actual = chooseOneResults({positions, poll: validPoll})
141- t.ok(actual.results[0].voters[pietId], 'valid vote is counted')
142- t.end()
143- })
144- )
145-})
146-
147-test('ChooseOneResults - A position stated after the closing time of the poll is not counted', function (t) {
148- const positions = [
149- { value: { content: {choice: 0, poll}, author: pietId, timestamp: now + 1 } }
150- ]
151-
152- pull(
153- pull.values(positions),
154- pull.asyncMap((fullPosition, cb) => {
155- ChooseOne(fullPosition.value.content, (err, position) => {
156- fullPosition.value.content = position
157- cb(err, fullPosition)
158- })
159- }),
160- pull.collect(postions => {
161- const actual = chooseOneResults({positions, poll: validPoll})
162- t.deepEqual(actual.results[0].voters, {}, 'invalid vote is not counted')
163- t.end()
164- })
165- )
166-})
167-
168-test('ChooseOneResults - A position stated after the closing time of the poll is included in the error object', function (t) {
169- const positions = [
170- { value: { content: {choice: 0, poll}, author: pietId, timestamp: now + 1 } }
171- ]
172-
173- pull(
174- pull.values(positions),
175- pull.asyncMap((fullPosition, cb) => {
176- ChooseOne(fullPosition.value.content, (err, position) => {
177- fullPosition.value.content = position
178- cb(err, fullPosition)
179- })
180- }),
181- pull.collect(postions => {
182- const actual = chooseOneResults({positions, poll: validPoll})
183- t.deepEqual(actual.errors[0].type, ERROR_POSITION_LATE, 'invalid vote is on error object')
184- t.end()
185- })
186- )
187-})
188-
189-test('ChooseOneResults - ChooseOneResults only counts latest vote by an author', function (t) {
190- const positions = [
191- { value: { content: {choice: 2, poll}, author: pietId } },
192- { value: { content: {choice: 0, poll}, author: pietId } }
193- ]
194-
195- pull(
196- pull.values(positions),
197- pull.asyncMap((fullPosition, cb) => {
198- ChooseOne(fullPosition.value.content, (err, position) => {
199- fullPosition.value.content = position
200- cb(err, fullPosition)
201- })
202- }),
203- pull.collect(postions => {
204- const actual = chooseOneResults({positions, poll: validPoll})
205- t.false(actual.results[2].voters[pietId], 'old vote is deleted')
206- t.true(actual.results[0].voters[pietId], 'new vote is counted')
207- t.end()
208- })
209- )
210-})
test/results/sync/buildResults.test.jsView
@@ -1,0 +1,210 @@
1+const test = require('tape')
2+const pull = require('pull-stream')
3+const ChooseOne = require('../../../position/async/buildChooseOne')()
4+const ChooseOnePoll = require('../../../poll/sync/buildChooseOne')
5+const chooseOneResults = require('../../../results/sync/buildResults')
6+const {isPosition, isPoll} = require('ssb-poll-schema')
7+const {ERROR_POSITION_CHOICE, ERROR_POSITION_LATE} = require('../../../types')
8+
9+const pietId = '@Mq8D3YC6VdErKQzV3oi2oK5hHSoIwR0hUQr4M46wr/0=.ed25519'
10+const mixId = '@Mq8D3YC6VdErKQzV3oi2oK5hHSoIwR0hUQr4M46wr/1=.ed25519'
11+const mikeyId = '@Mq8D3YC6VdErKQzV3oi2oK5hHSoIwR0hUQr4M46wr/2=.ed25519'
12+const timmyId = '@Mq8D3YC6VdErKQzV3oi2oK5hHSoIwR0hUQr4M46wr/3=.ed25519'
13+const tommyId = '@Mq8D3YC6VdErKQzV3oi2oK5hHSoIwR0hUQr4M46wr/4=.ed25519'
14+const sallyId = '@Mq8D3YC6VdErKQzV3oi2oK5hHSoIwR0hUQr4M46wr/5=.ed25519'
15+
16+const poll = '%t+PhrNxxXkw/jMo6mnwUWfFjJapoPWxzsQoe0Np+nYw=.sha256'
17+
18+const now = new Date().toISOString()
19+
20+const validPoll = {
21+ key: '%t+PhrNxxXkw/jMo6mnwUWfFjJapoPWxzsQoe0Np+nYw=.sha256',
22+ value: {
23+ content: ChooseOnePoll({
24+ choices: [1, 2, 'three'],
25+ title: 'how many food',
26+ closesAt: now
27+ })
28+ }
29+}
30+
31+test('ChooseOneResults - ChooseOneResults', function (t) {
32+ const positions = [
33+ { value: { content: {choice: 0, poll}, author: pietId } },
34+ { value: { content: {choice: 0, poll}, author: mixId } },
35+ { value: { content: {choice: 0, poll}, author: mikeyId } },
36+ { value: { content: {choice: 1, poll}, author: timmyId } },
37+ { value: { content: {choice: 1, poll}, author: tommyId } },
38+ { value: { content: {choice: 2, poll}, author: sallyId } }
39+ ]
40+
41+ const expected = {
42+ results: [
43+ {
44+ choice: 1,
45+ voters: {
46+ [pietId]: positions[0],
47+ [mixId]: positions[1],
48+ [mikeyId]: positions[2]
49+ }
50+ },
51+ {
52+ choice: 2,
53+ voters: {
54+ [timmyId]: positions[3],
55+ [tommyId]: positions[4]
56+ }
57+ },
58+ {
59+ choice: 'three',
60+ voters: {
61+ [sallyId]: positions[5]
62+ }
63+ }
64+ ],
65+ errors: {}
66+ }
67+
68+ pull(
69+ pull.values(positions),
70+ pull.asyncMap((fullPosition, cb) => {
71+ ChooseOne(fullPosition.value.content, (err, position) => {
72+ fullPosition.value.content = position
73+ cb(err, fullPosition)
74+ })
75+ }),
76+ pull.collect(postions => {
77+ const actual = chooseOneResults({positions, poll: validPoll})
78+ t.deepEqual(actual, expected, 'results are correct')
79+ t.end()
80+ })
81+ )
82+})
83+
84+test('ChooseOneResults - a position stated for an invalid choice index is not counted', function (t) {
85+ const positions = [
86+ { value: { content: {choice: 3, poll}, author: pietId } }
87+ ]
88+
89+ pull(
90+ pull.values(positions),
91+ pull.asyncMap((fullPosition, cb) => {
92+ ChooseOne(fullPosition.value.content, (err, position) => {
93+ fullPosition.value.content = position
94+ cb(err, fullPosition)
95+ })
96+ }),
97+ pull.collect(postions => {
98+ const actual = chooseOneResults({positions, poll: validPoll})
99+ t.false(actual.results[3], 'invalid vote is not counted')
100+ t.end()
101+ })
102+ )
103+})
104+
105+test('ChooseOneResults - a position stated for an invalid choice index is included in the errors object', function (t) {
106+ const positions = [
107+ { value: { content: {choice: 3, poll}, author: pietId } }
108+ ]
109+
110+ pull(
111+ pull.values(positions),
112+ pull.asyncMap((fullPosition, cb) => {
113+ ChooseOne(fullPosition.value.content, (err, position) => {
114+ fullPosition.value.content = position
115+ cb(err, fullPosition)
116+ })
117+ }),
118+ pull.collect(postions => {
119+ const actual = chooseOneResults({positions, poll: validPoll})
120+ t.deepEqual(actual.errors[0].type, ERROR_POSITION_CHOICE, 'invalid vote is on error object')
121+ t.end()
122+ })
123+ )
124+})
125+
126+test('ChooseOneResults - A position stated before the closing time of the poll is counted', function (t) {
127+ const positions = [
128+ { value: { content: {choice: 0, poll}, author: pietId, timestamp: now - 1 } }
129+ ]
130+
131+ pull(
132+ pull.values(positions),
133+ pull.asyncMap((fullPosition, cb) => {
134+ ChooseOne(fullPosition.value.content, (err, position) => {
135+ fullPosition.value.content = position
136+ cb(err, fullPosition)
137+ })
138+ }),
139+ pull.collect(postions => {
140+ const actual = chooseOneResults({positions, poll: validPoll})
141+ t.ok(actual.results[0].voters[pietId], 'valid vote is counted')
142+ t.end()
143+ })
144+ )
145+})
146+
147+test('ChooseOneResults - A position stated after the closing time of the poll is not counted', function (t) {
148+ const positions = [
149+ { value: { content: {choice: 0, poll}, author: pietId, timestamp: now + 1 } }
150+ ]
151+
152+ pull(
153+ pull.values(positions),
154+ pull.asyncMap((fullPosition, cb) => {
155+ ChooseOne(fullPosition.value.content, (err, position) => {
156+ fullPosition.value.content = position
157+ cb(err, fullPosition)
158+ })
159+ }),
160+ pull.collect(postions => {
161+ const actual = chooseOneResults({positions, poll: validPoll})
162+ t.deepEqual(actual.results[0].voters, {}, 'invalid vote is not counted')
163+ t.end()
164+ })
165+ )
166+})
167+
168+test('ChooseOneResults - A position stated after the closing time of the poll is included in the error object', function (t) {
169+ const positions = [
170+ { value: { content: {choice: 0, poll}, author: pietId, timestamp: now + 1 } }
171+ ]
172+
173+ pull(
174+ pull.values(positions),
175+ pull.asyncMap((fullPosition, cb) => {
176+ ChooseOne(fullPosition.value.content, (err, position) => {
177+ fullPosition.value.content = position
178+ cb(err, fullPosition)
179+ })
180+ }),
181+ pull.collect(postions => {
182+ const actual = chooseOneResults({positions, poll: validPoll})
183+ t.deepEqual(actual.errors[0].type, ERROR_POSITION_LATE, 'invalid vote is on error object')
184+ t.end()
185+ })
186+ )
187+})
188+
189+test('ChooseOneResults - ChooseOneResults only counts latest vote by an author', function (t) {
190+ const positions = [
191+ { value: { content: {choice: 2, poll}, author: pietId } },
192+ { value: { content: {choice: 0, poll}, author: pietId } }
193+ ]
194+
195+ pull(
196+ pull.values(positions),
197+ pull.asyncMap((fullPosition, cb) => {
198+ ChooseOne(fullPosition.value.content, (err, position) => {
199+ fullPosition.value.content = position
200+ cb(err, fullPosition)
201+ })
202+ }),
203+ pull.collect(postions => {
204+ const actual = chooseOneResults({positions, poll: validPoll})
205+ t.false(actual.results[2].voters[pietId], 'old vote is deleted')
206+ t.true(actual.results[0].voters[pietId], 'new vote is counted')
207+ t.end()
208+ })
209+ )
210+})
results/sync/buildResults.jsView
@@ -1,0 +1,72 @@
1+const getMsgContent = require('../../lib/getMsgContent')
2+const PositionChoiceError = require('../../errors/sync/positionChoiceError')
3+const PositionLateError = require('../../errors/sync/positionLateError')
4+const { isChooseOnePoll } = require('ssb-poll-schema')
5+
6+// Expects `poll` and `position` objects passed in to be of shape:
7+// {
8+// key,
9+// value: {
10+// content: {...},
11+// timestamp: ...
12+// author: ...
13+// }
14+// }
15+//
16+// postions must be of the correct type ie: type checked by the caller.
17+module.exports = function ({positions, poll}) {
18+ if (isChooseOnePoll(poll)) {
19+ return chooseOneResults({positions, poll})
20+ }
21+}
22+
23+function chooseOneResults ({positions, poll}) {
24+ var results = getMsgContent(poll)
25+ .details
26+ .choices
27+ .map(choice => {
28+ return {
29+ choice,
30+ voters: {}
31+ }
32+ })
33+
34+ return positions.reduce(function (acc, position) {
35+ const { author, content } = position.value
36+ const { choice } = content.details
37+
38+ if (isInvalidChoice({position, poll})) {
39+ acc.errors.push(PositionChoiceError({position}))
40+ return acc
41+ }
42+
43+ if (isPositionLate({position, poll})) {
44+ acc.errors.push(PositionLateError({position}))
45+ return acc
46+ }
47+
48+ deleteExistingVotesByAuthor({results: acc.results, author})
49+ acc.results[choice].voters[author] = position
50+
51+ return acc
52+ }, {errors: [], results})
53+}
54+
55+// !!! assumes these are already sorted by time.
56+// modifies results passed in
57+function deleteExistingVotesByAuthor ({author, results}) {
58+ results.forEach(result => {
59+ if (result.voters[author]) {
60+ delete result.voters[author]
61+ }
62+ })
63+}
64+
65+function isInvalidChoice ({position, poll}) {
66+ const { choice } = position.value.content.details
67+ return choice >= poll.value.content.details.choices.length
68+}
69+
70+function isPositionLate ({position, poll}) {
71+ return position.value.timestamp > poll.value.content.closesAt
72+}

Built with git-ssb-web