Skip to content

Commit 902eb49

Browse files
committed
Add unit tests for SigningService with actual worker
1 parent 90f46f3 commit 902eb49

File tree

1 file changed

+190
-0
lines changed

1 file changed

+190
-0
lines changed
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
import { toStreamID, utf8ToBinary } from '@streamr/utils'
2+
import { SignatureType } from '@streamr/trackerless-network'
3+
import { SigningService } from '../../src/signature/SigningService'
4+
import { SigningRequest } from '../../src/signature/signingUtils'
5+
import { StreamMessageType } from '../../src/protocol/StreamMessage'
6+
import { EthereumKeyPairIdentity } from '../../src/identity/EthereumKeyPairIdentity'
7+
import { DestroySignal } from '../../src/DestroySignal'
8+
import { createSignaturePayload } from '../../src/signature/createSignaturePayload'
9+
import { EcdsaSecp256k1Evm } from '@streamr/utils'
10+
11+
describe('SigningService', () => {
12+
13+
let signingService: SigningService
14+
let destroySignal: DestroySignal
15+
16+
beforeEach(() => {
17+
destroySignal = new DestroySignal()
18+
signingService = new SigningService(destroySignal)
19+
})
20+
21+
afterEach(() => {
22+
signingService.destroy()
23+
})
24+
25+
it('signs a message using the worker and produces a valid signature', async () => {
26+
const identity = EthereumKeyPairIdentity.generate()
27+
const privateKey = identity.getPrivateKey()
28+
const publisherId = await identity.getUserId()
29+
30+
const payloadInput = {
31+
messageId: {
32+
streamId: toStreamID('test-stream'),
33+
streamPartition: 0,
34+
timestamp: Date.now(),
35+
sequenceNumber: 0,
36+
publisherId,
37+
msgChainId: 'test-chain'
38+
},
39+
content: utf8ToBinary(JSON.stringify({ hello: 'world' })),
40+
messageType: StreamMessageType.MESSAGE
41+
}
42+
43+
const request: SigningRequest = {
44+
payloadInput,
45+
privateKey,
46+
signatureType: SignatureType.ECDSA_SECP256K1_EVM
47+
}
48+
49+
const result = await signingService.sign(request)
50+
51+
if (result.type !== 'success') {
52+
throw new Error(`Expected success but got error: ${result.message}`)
53+
}
54+
expect(result.signature).toBeInstanceOf(Uint8Array)
55+
expect(result.signature.length).toBeGreaterThan(0)
56+
57+
// Verify the signature is valid by checking it against the payload
58+
const payload = createSignaturePayload(payloadInput)
59+
const signingUtil = new EcdsaSecp256k1Evm()
60+
const isValid = await signingUtil.verifySignature(await identity.getUserIdRaw(), payload, result.signature)
61+
expect(isValid).toBe(true)
62+
})
63+
64+
it('can sign multiple messages sequentially', async () => {
65+
const identity = EthereumKeyPairIdentity.generate()
66+
const privateKey = identity.getPrivateKey()
67+
const publisherId = await identity.getUserId()
68+
69+
const signatures: Uint8Array[] = []
70+
71+
for (let i = 0; i < 3; i++) {
72+
const request: SigningRequest = {
73+
payloadInput: {
74+
messageId: {
75+
streamId: toStreamID('test-stream'),
76+
streamPartition: 0,
77+
timestamp: Date.now() + i,
78+
sequenceNumber: i,
79+
publisherId,
80+
msgChainId: 'test-chain'
81+
},
82+
content: utf8ToBinary(JSON.stringify({ index: i })),
83+
messageType: StreamMessageType.MESSAGE
84+
},
85+
privateKey,
86+
signatureType: SignatureType.ECDSA_SECP256K1_EVM
87+
}
88+
89+
const result = await signingService.sign(request)
90+
if (result.type !== 'success') {
91+
throw new Error(`Expected success but got error: ${result.message}`)
92+
}
93+
signatures.push(result.signature)
94+
}
95+
96+
expect(signatures).toHaveLength(3)
97+
// All signatures should be different (different payloads)
98+
expect(new Set(signatures.map(s => Buffer.from(s).toString('hex'))).size).toBe(3)
99+
})
100+
101+
it('returns error for unsupported signature type', async () => {
102+
const identity = EthereumKeyPairIdentity.generate()
103+
const privateKey = identity.getPrivateKey()
104+
const publisherId = await identity.getUserId()
105+
106+
const request: SigningRequest = {
107+
payloadInput: {
108+
messageId: {
109+
streamId: toStreamID('test-stream'),
110+
streamPartition: 0,
111+
timestamp: Date.now(),
112+
sequenceNumber: 0,
113+
publisherId,
114+
msgChainId: 'test-chain'
115+
},
116+
content: utf8ToBinary(JSON.stringify({ hello: 'world' })),
117+
messageType: StreamMessageType.MESSAGE
118+
},
119+
privateKey,
120+
signatureType: 999 as SignatureType // Invalid signature type
121+
}
122+
123+
const result = await signingService.sign(request)
124+
125+
if (result.type !== 'error') {
126+
throw new Error('Expected error but got success')
127+
}
128+
expect(result.message).toContain('Unsupported signatureType')
129+
})
130+
131+
it('cleans up worker on destroy', async () => {
132+
const identity = EthereumKeyPairIdentity.generate()
133+
const privateKey = identity.getPrivateKey()
134+
const publisherId = await identity.getUserId()
135+
136+
// First sign to ensure worker is created
137+
const request: SigningRequest = {
138+
payloadInput: {
139+
messageId: {
140+
streamId: toStreamID('test-stream'),
141+
streamPartition: 0,
142+
timestamp: Date.now(),
143+
sequenceNumber: 0,
144+
publisherId,
145+
msgChainId: 'test-chain'
146+
},
147+
content: utf8ToBinary(JSON.stringify({ hello: 'world' })),
148+
messageType: StreamMessageType.MESSAGE
149+
},
150+
privateKey,
151+
signatureType: SignatureType.ECDSA_SECP256K1_EVM
152+
}
153+
154+
await signingService.sign(request)
155+
156+
// Destroy should not throw
157+
expect(() => signingService.destroy()).not.toThrow()
158+
159+
// Calling destroy again should be safe (idempotent)
160+
expect(() => signingService.destroy()).not.toThrow()
161+
})
162+
163+
it('cleans up via DestroySignal', async () => {
164+
const identity = EthereumKeyPairIdentity.generate()
165+
const privateKey = identity.getPrivateKey()
166+
const publisherId = await identity.getUserId()
167+
168+
const request: SigningRequest = {
169+
payloadInput: {
170+
messageId: {
171+
streamId: toStreamID('test-stream'),
172+
streamPartition: 0,
173+
timestamp: Date.now(),
174+
sequenceNumber: 0,
175+
publisherId,
176+
msgChainId: 'test-chain'
177+
},
178+
content: utf8ToBinary(JSON.stringify({ hello: 'world' })),
179+
messageType: StreamMessageType.MESSAGE
180+
},
181+
privateKey,
182+
signatureType: SignatureType.ECDSA_SECP256K1_EVM
183+
}
184+
185+
await signingService.sign(request)
186+
187+
// Trigger destroy via signal - should not throw
188+
await destroySignal.destroy()
189+
})
190+
})

0 commit comments

Comments
 (0)