synapse 2.173.1__py311-none-any.whl → 2.174.0__py311-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of synapse might be problematic. Click here for more details.

Files changed (44) hide show
  1. synapse/axon.py +1 -1
  2. synapse/common.py +19 -5
  3. synapse/cortex.py +46 -10
  4. synapse/lib/agenda.py +6 -0
  5. synapse/lib/ast.py +1 -1
  6. synapse/lib/lmdbslab.py +11 -1
  7. synapse/lib/modelrev.py +17 -1
  8. synapse/lib/modules.py +1 -0
  9. synapse/lib/msgpack.py +25 -3
  10. synapse/lib/nexus.py +26 -22
  11. synapse/lib/schemas.py +31 -0
  12. synapse/lib/stormsvc.py +30 -11
  13. synapse/lib/stormtypes.py +23 -9
  14. synapse/lib/trigger.py +0 -4
  15. synapse/lib/version.py +2 -2
  16. synapse/lib/view.py +2 -0
  17. synapse/models/crypto.py +22 -0
  18. synapse/models/economic.py +23 -2
  19. synapse/models/entity.py +16 -0
  20. synapse/models/files.py +4 -1
  21. synapse/models/geopol.py +3 -0
  22. synapse/models/orgs.py +3 -4
  23. synapse/tests/test_cortex.py +13 -0
  24. synapse/tests/test_lib_agenda.py +129 -1
  25. synapse/tests/test_lib_ast.py +21 -0
  26. synapse/tests/test_lib_grammar.py +4 -0
  27. synapse/tests/test_lib_httpapi.py +1 -0
  28. synapse/tests/test_lib_lmdbslab.py +16 -1
  29. synapse/tests/test_lib_modelrev.py +57 -0
  30. synapse/tests/test_lib_msgpack.py +58 -8
  31. synapse/tests/test_lib_nexus.py +44 -1
  32. synapse/tests/test_lib_storm.py +7 -7
  33. synapse/tests/test_lib_stormsvc.py +128 -51
  34. synapse/tests/test_lib_stormtypes.py +43 -4
  35. synapse/tests/test_lib_trigger.py +23 -4
  36. synapse/tests/test_model_crypto.py +6 -0
  37. synapse/tests/test_model_economic.py +14 -1
  38. synapse/tests/test_model_geopol.py +3 -0
  39. synapse/tools/changelog.py +236 -0
  40. {synapse-2.173.1.dist-info → synapse-2.174.0.dist-info}/METADATA +1 -1
  41. {synapse-2.173.1.dist-info → synapse-2.174.0.dist-info}/RECORD +44 -42
  42. {synapse-2.173.1.dist-info → synapse-2.174.0.dist-info}/WHEEL +1 -1
  43. {synapse-2.173.1.dist-info → synapse-2.174.0.dist-info}/LICENSE +0 -0
  44. {synapse-2.173.1.dist-info → synapse-2.174.0.dist-info}/top_level.txt +0 -0
synapse/models/crypto.py CHANGED
@@ -371,31 +371,53 @@ class CryptoModule(s_module.CoreModule):
371
371
  ('crypto:algorithm', {}, ()),
372
372
 
373
373
  ('crypto:key', {}, (
374
+
374
375
  ('algorithm', ('crypto:algorithm', {}), {
375
376
  'ex': 'aes256',
376
377
  'doc': 'The cryptographic algorithm which uses the key material.'}),
378
+
377
379
  ('mode', ('str', {'lower': True, 'onespace': True}), {
378
380
  'doc': 'The algorithm specific mode in use.'}),
381
+
379
382
  ('iv', ('hex', {}), {
380
383
  'doc': 'The hex encoded initialization vector.'}),
384
+
385
+ ('iv:text', ('it:dev:str', {}), {
386
+ 'doc': 'Set only if the :iv property decodes to ASCII.'}),
387
+
381
388
  ('public', ('hex', {}), {
382
389
  'doc': 'The hex encoded public key material if the algorithm has a public/private key pair.'}),
390
+
391
+ ('public:text', ('it:dev:str', {}), {
392
+ 'doc': 'Set only if the :public property decodes to ASCII.'}),
393
+
383
394
  ('public:md5', ('hash:md5', {}), {
384
395
  'doc': 'The MD5 hash of the public key in raw binary form.'}),
396
+
385
397
  ('public:sha1', ('hash:sha1', {}), {
386
398
  'doc': 'The SHA1 hash of the public key in raw binary form.'}),
399
+
387
400
  ('public:sha256', ('hash:sha256', {}), {
388
401
  'doc': 'The SHA256 hash of the public key in raw binary form.'}),
402
+
389
403
  ('private', ('hex', {}), {
390
404
  'doc': 'The hex encoded private key material. All symmetric keys are private.'}),
405
+
406
+ ('private:text', ('it:dev:str', {}), {
407
+ 'doc': 'Set only if the :private property decodes to ASCII.'}),
408
+
391
409
  ('private:md5', ('hash:md5', {}), {
392
410
  'doc': 'The MD5 hash of the private key in raw binary form.'}),
411
+
393
412
  ('private:sha1', ('hash:sha1', {}), {
394
413
  'doc': 'The SHA1 hash of the private key in raw binary form.'}),
414
+
395
415
  ('private:sha256', ('hash:sha256', {}), {
396
416
  'doc': 'The SHA256 hash of the private key in raw binary form.'}),
417
+
397
418
  ('seed:passwd', ('inet:passwd', {}), {
398
419
  'doc': 'The seed password used to generate the key material.'}),
420
+
399
421
  ('seed:algorithm', ('crypto:algorithm', {}), {
400
422
  'ex': 'pbkdf2',
401
423
  'doc': 'The algorithm used to generate the key from the seed password.'})
@@ -48,11 +48,11 @@ class EconModule(s_module.CoreModule):
48
48
  'doc': 'An invoice issued requesting payment.'}),
49
49
 
50
50
  ('econ:price', ('hugenum', {'norm': False}), {
51
- 'doc': 'The amount of money expected, required, or given in payment for something',
51
+ 'doc': 'The amount of money expected, required, or given in payment for something.',
52
52
  'ex': '2.20'}),
53
53
 
54
54
  ('econ:currency', ('str', {'lower': True, 'strip': False}), {
55
- 'doc': 'The name of a system of money in general use',
55
+ 'doc': 'The name of a system of money in general use.',
56
56
  'ex': 'usd'}),
57
57
 
58
58
  ('econ:fin:exchange', ('guid', {}), {
@@ -99,6 +99,7 @@ class EconModule(s_module.CoreModule):
99
99
 
100
100
  'forms': (
101
101
 
102
+ ('econ:currency', {}, ()),
102
103
  ('econ:pay:iin', {}, (
103
104
 
104
105
  ('org', ('ou:org', {}), {
@@ -205,6 +206,9 @@ class EconModule(s_module.CoreModule):
205
206
  ('fee', ('econ:price', {}), {
206
207
  'doc': 'The transaction fee paid by the recipient to the payment processor.'}),
207
208
 
209
+ ('from:cash', ('bool', {}), {
210
+ 'doc': 'Set to true if the payment input was in cash.'}),
211
+
208
212
  ('from:account', ('econ:bank:account', {}), {
209
213
  'doc': 'The bank account which made the payment.'}),
210
214
 
@@ -220,6 +224,9 @@ class EconModule(s_module.CoreModule):
220
224
  ('from:contact', ('ps:contact', {}), {
221
225
  'doc': 'Contact information for the entity making the payment.'}),
222
226
 
227
+ ('to:cash', ('bool', {}), {
228
+ 'doc': 'Set to true if the payment output was in cash.'}),
229
+
223
230
  ('to:account', ('econ:bank:account', {}), {
224
231
  'doc': 'The bank account which received the payment.'}),
225
232
 
@@ -256,6 +263,20 @@ class EconModule(s_module.CoreModule):
256
263
  ('receipt', ('econ:acct:receipt', {}), {
257
264
  'doc': 'The receipt that was issued for the payment.'}),
258
265
 
266
+ ('place', ('geo:place', {}), {
267
+ 'doc': 'The place where the payment occurred.'}),
268
+
269
+ ('place:name', ('geo:name', {}), {
270
+ 'doc': 'The name of the place where the payment occurred.'}),
271
+
272
+ ('place:address', ('geo:address', {}), {
273
+ 'doc': 'The address of the place where the payment occurred.'}),
274
+
275
+ ('place:loc', ('loc', {}), {
276
+ 'doc': 'The loc of the place where the payment occurred.'}),
277
+
278
+ ('place:latlong', ('geo:latlong', {}), {
279
+ 'doc': 'The latlong where the payment occurred.'}),
259
280
  )),
260
281
 
261
282
  ('econ:acct:balance', {}, (
@@ -0,0 +1,16 @@
1
+ import synapse.lib.module as s_module
2
+
3
+ class EntityModule(s_module.CoreModule):
4
+
5
+ def getModelDefs(self):
6
+ return (('entity', {
7
+
8
+ 'types': (
9
+ ('entity:name', ('str', {'onespace': True, 'lower': True}), {
10
+ 'doc': 'A name used to refer to an entity.'}),
11
+ ),
12
+
13
+ 'forms': (
14
+ ('entity:name', {}, ()),
15
+ ),
16
+ }),)
synapse/models/files.py CHANGED
@@ -412,7 +412,10 @@ class FileModule(s_module.CoreModule):
412
412
  'doc': 'The GUID of the metadata pulled from a Windows shortcut or LNK file.',
413
413
  }),
414
414
  ),
415
-
415
+ 'edges': (
416
+ (('file:bytes', 'refs', 'it:dev:str'), {
417
+ 'doc': 'The source file contains the target string.'}),
418
+ ),
416
419
  'forms': (
417
420
 
418
421
  ('file:bytes', {}, (
synapse/models/geopol.py CHANGED
@@ -88,6 +88,9 @@ class PolModule(s_module.CoreModule):
88
88
 
89
89
  ('vitals', ('pol:vitals', {}), {
90
90
  'doc': 'The most recent known vitals for the country.'}),
91
+
92
+ ('currencies', ('array', {'type': 'econ:currency', 'sorted': True, 'uniq': True}), {
93
+ 'doc': 'The official currencies used in the country.'}),
91
94
  )),
92
95
  ('pol:immigration:status:type:taxonomy', {}, ()),
93
96
  ('pol:immigration:status', {}, (
synapse/models/orgs.py CHANGED
@@ -731,8 +731,7 @@ class OuModule(s_module.CoreModule):
731
731
  ('contact', ('ps:contact', {}), {
732
732
  'doc': 'The contact info for the person who holds the position.',
733
733
  }),
734
- # TODO migrate to ou:jobtitle
735
- ('title', ('str', {'lower': True, 'onespace': True}), {
734
+ ('title', ('ou:jobtitle', {}), {
736
735
  'doc': 'The title of the position.',
737
736
  }),
738
737
  ('reports', ('array', {'type': 'ou:position', 'uniq': True, 'sorted': True}), {
@@ -1024,11 +1023,11 @@ class OuModule(s_module.CoreModule):
1024
1023
  ('sponsors', ('array', {'type': 'ps:contact', 'uniq': True, 'sorted': True}), {
1025
1024
  'doc': 'An array of contacts which sponsored the conference.',
1026
1025
  }),
1027
- ('name', ('str', {'lower': True}), {
1026
+ ('name', ('entity:name', {}), {
1028
1027
  'doc': 'The full name of the conference.',
1029
1028
  'ex': 'defcon 2017'}),
1030
1029
 
1031
- ('names', ('array', {'type': 'str', 'typeopts': {'lower': True}, 'uniq': True, 'sorted': True}), {
1030
+ ('names', ('array', {'type': 'entity:name', 'uniq': True, 'sorted': True}), {
1032
1031
  'doc': 'An array of alternate names for the conference.'}),
1033
1032
 
1034
1033
  ('desc', ('str', {'lower': True}), {
@@ -269,6 +269,19 @@ class CortexTest(s_t_utils.SynTest):
269
269
  self.eq(await core00.getJsonObj('foo/bar'), 'zoinks')
270
270
  self.eq(await core01.getJsonObj('foo/bar'), 'zoinks')
271
271
 
272
+ # Test startup sequencing. We must create the child cells prior to
273
+ # the nexus recover() call from occuring :)
274
+ with self.getTestDir() as dirn:
275
+ async with self.getTestCore(dirn=dirn) as core:
276
+ await core.callStorm('$lib.jsonstor.set((path,), hehe)')
277
+
278
+ with self.getAsyncLoggerStream('synapse.lib.nexus') as stream:
279
+ async with self.getTestCore(dirn=dirn) as core:
280
+ q = 'return( $lib.jsonstor.get((path,)) )'
281
+ self.eq('hehe', await core.callStorm(q))
282
+ stream.seek(0)
283
+ self.notin('Exception while replaying log', stream.read())
284
+
272
285
  async def test_cortex_layer_mirror(self):
273
286
 
274
287
  # test a layer mirror from a layer
@@ -331,7 +331,17 @@ class AgendaTest(s_t_utils.SynTest):
331
331
  appt = await agenda.get(guid)
332
332
  self.eq(appt.isrunning, True)
333
333
  self.eq(core.view.iden, appt.task.info.get('view'))
334
- await appt.task.kill()
334
+
335
+ self.true(await core._killCronTask(guid))
336
+
337
+ events = [
338
+ {'event': 'cron:stop', 'info': {'iden': appt.iden}},
339
+ ]
340
+
341
+ task = core.schedCoro(s_t_utils.waitForBehold(core, events))
342
+ await asyncio.wait_for(task, timeout=5)
343
+
344
+ self.false(await core._killCronTask(guid))
335
345
 
336
346
  appt = await agenda.get(guid)
337
347
  self.eq(appt.isrunning, False)
@@ -798,3 +808,121 @@ class AgendaTest(s_t_utils.SynTest):
798
808
  stream.seek(0)
799
809
  data = stream.read()
800
810
  self.isin("_Appt.edits() Invalid attribute received: invalid = 'newp'", data)
811
+
812
+ async def test_cron_kill(self):
813
+ async with self.getTestCore() as core:
814
+
815
+ data = []
816
+ evt = asyncio.Event()
817
+
818
+ async def task():
819
+ async for mesg in core.behold():
820
+ data.append(mesg)
821
+ if mesg.get('event') == 'cron:stop':
822
+ evt.set()
823
+
824
+ core.schedCoro(task())
825
+
826
+ q = '$q=$lib.queue.gen(test) for $i in $lib.range(60) { $lib.time.sleep(0.1) $q.put($i) }'
827
+ guid = s_common.guid()
828
+ cdef = {
829
+ 'creator': core.auth.rootuser.iden, 'iden': guid,
830
+ 'storm': q,
831
+ 'reqs': {'now': True}
832
+ }
833
+ await core.addCronJob(cdef)
834
+
835
+ q = '$q=$lib.queue.gen(test) for $valu in $q.get((0), wait=(true)) { return ($valu) }'
836
+ valu = await core.callStorm(q)
837
+ self.eq(valu, 0)
838
+
839
+ opts = {'vars': {'iden': guid}}
840
+ get_cron = 'return($lib.cron.get($iden).pack())'
841
+ cdef = await core.callStorm(get_cron, opts=opts)
842
+ self.true(cdef.get('isrunning'))
843
+
844
+ self.true(await core.callStorm('return($lib.cron.get($iden).kill())', opts=opts))
845
+
846
+ self.true(await asyncio.wait_for(evt.wait(), timeout=12))
847
+
848
+ cdef = await core.callStorm(get_cron, opts=opts)
849
+ self.false(cdef.get('isrunning'))
850
+
851
+ async def test_cron_kill_pool(self):
852
+
853
+ async with self.getTestAhaProv() as aha:
854
+
855
+ import synapse.cortex as s_cortex
856
+ import synapse.lib.base as s_base
857
+
858
+ async with await s_base.Base.anit() as base:
859
+
860
+ with self.getTestDir() as dirn:
861
+
862
+ dirn00 = s_common.genpath(dirn, 'cell00')
863
+ dirn01 = s_common.genpath(dirn, 'cell01')
864
+
865
+ core00 = await base.enter_context(self.addSvcToAha(aha, '00.core', s_cortex.Cortex, dirn=dirn00))
866
+ provinfo = {'mirror': '00.core'}
867
+ core01 = await base.enter_context(self.addSvcToAha(aha, '01.core', s_cortex.Cortex, dirn=dirn01, provinfo=provinfo))
868
+
869
+ self.len(1, await core00.nodes('[inet:asn=0]'))
870
+ await core01.sync()
871
+ self.len(1, await core01.nodes('inet:asn=0'))
872
+
873
+ msgs = await core00.stormlist('aha.pool.add pool00...')
874
+ self.stormHasNoWarnErr(msgs)
875
+ self.stormIsInPrint('Created AHA service pool: pool00.loop.vertex.link', msgs)
876
+
877
+ msgs = await core00.stormlist('aha.pool.svc.add pool00... 01.core...')
878
+ self.stormHasNoWarnErr(msgs)
879
+ self.stormIsInPrint('AHA service (01.core...) added to service pool (pool00.loop.vertex.link)', msgs)
880
+
881
+ msgs = await core00.stormlist('cortex.storm.pool.set --connection-timeout 1 --sync-timeout 1 aha://pool00...')
882
+ self.stormHasNoWarnErr(msgs)
883
+ self.stormIsInPrint('Storm pool configuration set.', msgs)
884
+
885
+ await core00.stormpool.waitready(timeout=12)
886
+
887
+ data = []
888
+ evt = asyncio.Event()
889
+
890
+ async def task():
891
+ async for mesg in core00.behold():
892
+ data.append(mesg)
893
+ if mesg.get('event') == 'cron:stop':
894
+ evt.set()
895
+
896
+ core00.schedCoro(task())
897
+
898
+ q = '$q=$lib.queue.gen(test) for $i in $lib.range(60) { $lib.time.sleep(0.1) $q.put($i) }'
899
+ guid = s_common.guid()
900
+ cdef = {
901
+ 'creator': core00.auth.rootuser.iden, 'iden': guid,
902
+ 'storm': q,
903
+ 'reqs': {'NOW': True},
904
+ 'pool': True,
905
+ }
906
+ await core00.addCronJob(cdef)
907
+
908
+ q = '$q=$lib.queue.gen(test) for $valu in $q.get((0), wait=(true)) { return ($valu) }'
909
+ valu = await core00.callStorm(q, opts={'mirror': False})
910
+ self.eq(valu, 0)
911
+
912
+ opts = {'vars': {'iden': guid}, 'mirror': False}
913
+ get_cron = 'return($lib.cron.get($iden).pack())'
914
+ cdef = await core00.callStorm(get_cron, opts=opts)
915
+ self.true(cdef.get('isrunning'))
916
+
917
+ self.true(await core00.callStorm('return($lib.cron.get($iden).kill())', opts=opts))
918
+
919
+ self.true(await asyncio.wait_for(evt.wait(), timeout=12))
920
+
921
+ cdef00 = await core00.callStorm(get_cron, opts=opts)
922
+ self.false(cdef00.get('isrunning'))
923
+
924
+ cdef01 = await core01.callStorm(get_cron, opts=opts)
925
+ self.false(cdef01.get('isrunning'))
926
+ self.eq(cdef01.get('lastresult'), 'cancelled')
927
+ self.gt(cdef00['laststarttime'], 0)
928
+ self.eq(cdef00['laststarttime'], cdef01['laststarttime'])
@@ -356,6 +356,27 @@ class AstTest(s_test.SynTest):
356
356
  self.len(1, nodes)
357
357
  self.none(nodes[0].get('.seen'))
358
358
 
359
+ # array var filter
360
+ q = '''
361
+ [(test:arrayprop=* :strs=(neato, burrito))
362
+ (test:arrayprop=* :ints=(1,2,3,4,5))] |
363
+ $pvar = "strs"
364
+ +:$pvar*[=neato]
365
+ '''
366
+ nodes = await core.nodes(q)
367
+ self.len(1, nodes)
368
+ self.eq(('neato', 'burrito'), nodes[0].get('strs'))
369
+
370
+ q = '$pvar=ints $avar=4 test:arrayprop +:$pvar*[=$avar]'
371
+ nodes = await core.nodes(q)
372
+ self.len(1, nodes)
373
+ self.eq((1, 2, 3, 4, 5), nodes[0].get('ints'))
374
+
375
+ q = '$pvar=strs $avar=burr test:arrayprop +:$pvar*[^=$avar]'
376
+ nodes = await core.nodes(q)
377
+ self.len(1, nodes)
378
+ self.eq(('neato', 'burrito'), nodes[0].get('strs'))
379
+
359
380
  # Sad paths
360
381
  q = '[test:str=newp -.newp]'
361
382
  await self.asyncraises(s_exc.NoSuchProp, core.nodes(q))
@@ -721,6 +721,8 @@ Queries = [
721
721
  '$foo=({"bar": null})',
722
722
  '$p="names" ps:contact:name=foo [ :$p?+=bar ]',
723
723
  '$p="names" ps:contact:name=foo [ :$p?-=bar ]',
724
+ '$pvar=stuff test:arrayprop +:$pvar*[=neato]',
725
+ '$pvar=ints test:arrayprop +:$pvar*[=$othervar]',
724
726
  ]
725
727
 
726
728
  # Generated with print_parse_list below
@@ -1346,6 +1348,8 @@ _ParseResults = [
1346
1348
  'Query: [SetVarOper: [Const: foo, DollarExpr: [ExprDict: [Const: bar, Const: None]]]]',
1347
1349
  'Query: [SetVarOper: [Const: p, Const: names], LiftPropBy: [Const: ps:contact:name, Const: =, Const: foo], EditPropSet: [RelProp: [VarValue: [Const: p]], Const: ?+=, Const: bar]]',
1348
1350
  'Query: [SetVarOper: [Const: p, Const: names], LiftPropBy: [Const: ps:contact:name, Const: =, Const: foo], EditPropSet: [RelProp: [VarValue: [Const: p]], Const: ?-=, Const: bar]]',
1351
+ 'Query: [SetVarOper: [Const: pvar, Const: stuff], LiftProp: [Const: test:arrayprop], FiltOper: [Const: +, ArrayCond: [RelProp: [VarValue: [Const: pvar]], Const: =, Const: neato]]]',
1352
+ 'Query: [SetVarOper: [Const: pvar, Const: ints], LiftProp: [Const: test:arrayprop], FiltOper: [Const: +, ArrayCond: [RelProp: [VarValue: [Const: pvar]], Const: =, VarValue: [Const: othervar]]]]',
1349
1353
  ]
1350
1354
 
1351
1355
  class GrammarTest(s_t_utils.SynTest):
@@ -916,6 +916,7 @@ class HttpApiTest(s_tests.SynTest):
916
916
  await core.callStorm('$c = $lib.cron.get($cron) $c.set("doc", "some docs")', opts=opts)
917
917
  await core.callStorm('cron.del $cron', opts=opts)
918
918
 
919
+ await core.addStormPkg(spkg)
919
920
  await core.addStormPkg(spkg)
920
921
  await core.addStormSvc(ssvc)
921
922
 
@@ -1,4 +1,5 @@
1
1
  import os
2
+ import json
2
3
  import asyncio
3
4
  import pathlib
4
5
  import multiprocessing
@@ -350,7 +351,7 @@ class LmdbSlabTest(s_t_utils.SynTest):
350
351
  with self.getTestDir() as dirn, patch('synapse.lib.lmdbslab.Slab.WARN_COMMIT_TIME_MS', 1), \
351
352
  patch('synapse.common.now', self.simplenow):
352
353
  path = os.path.join(dirn, 'test.lmdb')
353
- with self.getAsyncLoggerStream('synapse.lib.lmdbslab', 'Commit with') as stream:
354
+ with self.getStructuredAsyncLoggerStream('synapse.lib.lmdbslab', 'Commit with') as stream:
354
355
  async with await s_lmdbslab.Slab.anit(path, map_size=100000) as slab:
355
356
  foo = slab.initdb('foo', dupsort=True)
356
357
  byts = b'\x00' * 256
@@ -358,6 +359,20 @@ class LmdbSlabTest(s_t_utils.SynTest):
358
359
  slab.put(b'\xff\xff\xff\xff' + s_common.guid(i).encode('utf8'), byts, db=foo)
359
360
  self.true(await stream.wait(timeout=1))
360
361
 
362
+ data = stream.getvalue()
363
+ msgs = [json.loads(m) for m in data.split('\\n') if m]
364
+ self.gt(len(msgs), 0)
365
+ self.nn(msgs[0].get('delta'))
366
+ self.nn(msgs[0].get('path'))
367
+ self.nn(msgs[0].get('xactopslen'))
368
+ self.sorteq([
369
+ 'vm.swappiness',
370
+ 'vm.dirty_expire_centisecs',
371
+ 'vm.dirty_writeback_centisecs',
372
+ 'vm.dirty_background_ratio',
373
+ 'vm.dirty_ratio',
374
+ ], msgs[0].get('sysctls', {}).keys())
375
+
361
376
  async def test_lmdbslab_max_replay(self):
362
377
  with self.getTestDir() as dirn:
363
378
  path = os.path.join(dirn, 'test.lmdb')
@@ -473,3 +473,60 @@ class ModelRevTest(s_tests.SynTest):
473
473
  nodes = await core.nodes('it:mitre:attack:technique=T0100')
474
474
  self.len(1, nodes)
475
475
  self.eq('lockpicking', nodes[0].get('name'))
476
+
477
+ async def test_modelrev_0_2_25(self):
478
+ async with self.getRegrCore('model-0.2.25') as core:
479
+
480
+ self.len(1, await core.nodes('econ:currency=usd'))
481
+
482
+ nodes = await core.nodes('ou:conference')
483
+ self.len(3, nodes)
484
+ names = [n.get('name') for n in nodes]
485
+ self.sorteq(names, (
486
+ 'sleuthcon',
487
+ 'defcon',
488
+ 'recon',
489
+ ))
490
+
491
+ namess = [n.get('names') for n in nodes]
492
+ self.sorteq(namess, (
493
+ ('defcon 2024',),
494
+ ('recon 2024 conference',),
495
+ ('sleuthcon 2024',),
496
+ ))
497
+
498
+ connames = (
499
+ 'sleuthcon', 'sleuthcon 2024',
500
+ 'defcon', 'defcon 2024',
501
+ 'recon', 'recon 2024 conference',
502
+ )
503
+
504
+ nodes = await core.nodes('entity:name')
505
+ self.len(6, nodes)
506
+ names = [n.ndef[1] for n in nodes]
507
+ self.sorteq(names, connames)
508
+
509
+ nodes = await core.nodes('ou:conference -> entity:name')
510
+ self.len(6, nodes)
511
+ names = [n.ndef[1] for n in nodes]
512
+ self.sorteq(names, connames)
513
+
514
+ positions = (
515
+ 'president of the united states',
516
+ 'vice president of the united states',
517
+ )
518
+
519
+ nodes = await core.nodes('ou:position')
520
+ self.len(2, nodes)
521
+ titles = [n.get('title') for n in nodes]
522
+ self.sorteq(titles, positions)
523
+
524
+ nodes = await core.nodes('ou:jobtitle')
525
+ self.len(2, nodes)
526
+ titles = [n.ndef[1] for n in nodes]
527
+ self.sorteq(titles, positions)
528
+
529
+ nodes = await core.nodes('ou:position -> ou:jobtitle')
530
+ self.len(2, nodes)
531
+ titles = [n.ndef[1] for n in nodes]
532
+ self.sorteq(titles, positions)
@@ -17,6 +17,50 @@ class MsgPackTest(s_t_utils.SynTest):
17
17
  byts = s_msgpack._fallback_en(('hehe', 10))
18
18
  self.eq(byts, b'\x92\xa4hehe\n')
19
19
 
20
+ def test_msgpack_ext(self):
21
+ valu = 0xffffffffffffffffffffffffffffffff
22
+ item = ('woot', valu)
23
+ byts = s_msgpack.en(item)
24
+ self.eq(item, s_msgpack.un(byts))
25
+ self.eq(byts, s_msgpack._fallback_en(item))
26
+
27
+ unpk = s_msgpack.Unpk()
28
+ self.eq(((24, item),), unpk.feed(byts))
29
+ with self.raises(s_exc.SynErr):
30
+ s_msgpack._ext_un(99, b'red baloons')
31
+
32
+ # Negative number support as well.
33
+ negvalu = -1 * valu
34
+ negitem = ('woot', negvalu)
35
+ negbytes = s_msgpack.en(negitem)
36
+ self.eq(negitem, s_msgpack.un(negbytes))
37
+ self.eq(negbytes, s_msgpack._fallback_en(negitem))
38
+
39
+ # Check across item.bit_length() boundaries
40
+ v = 0xffffffffffffffff
41
+ for i in (1, 0xffffffffffffffff + 1, 0xffffffffffffffff + 2):
42
+ nv = v + i
43
+ buf = s_msgpack.en(nv)
44
+ self.eq(nv, s_msgpack.un(buf))
45
+ v = -0x8000000000000000
46
+ for i in (1, 0x7fffffffffffffff, 0x7fffffffffffffff + 1):
47
+ nv = v - i
48
+ buf = s_msgpack.en(nv)
49
+ self.eq(nv, s_msgpack.un(buf))
50
+
51
+ # We can also support values > 128 bits in width
52
+ valu = 0xfffffffffffffffffffffffffffffffff
53
+ item = ('woot', valu)
54
+ byts = s_msgpack.en(item)
55
+ self.eq(item, s_msgpack.un(byts))
56
+ self.eq(byts, s_msgpack._fallback_en(item))
57
+
58
+ negvalu = -1 * valu
59
+ negitem = ('woot', negvalu)
60
+ negbytes = s_msgpack.en(negitem)
61
+ self.eq(negitem, s_msgpack.un(negbytes))
62
+ self.eq(negbytes, s_msgpack._fallback_en(negitem))
63
+
20
64
  def test_msgpack_un(self):
21
65
  item = s_msgpack.un(b'\x92\xa4hehe\n')
22
66
  self.eq(item, ('hehe', 10))
@@ -106,8 +150,8 @@ class MsgPackTest(s_t_utils.SynTest):
106
150
  self.checkLoadfile(s_msgpack._fallback_en)
107
151
 
108
152
  def checkTypes(self, enfunc):
109
- # This is a future-proofing test for msgpack to ensure that
110
- buf = b'\x92\xa4hehe\x85\xa3str\xa41234\xa3int\xcd\x04\xd2\xa5float\xcb@(\xae\x14z\xe1G\xae\xa3bin\xc4\x041234\xa9realworld\xac\xc7\x8b\xef\xbf\xbd\xed\xa1\x82\xef\xbf\xbd\x12'
153
+ # This is a future-proofing test for msgpack to ensure that we have stability with msgpack-python
154
+ buf = b'\x92\xa4hehe\x8b\xa3str\xa41234\xa3int\xcd\x04\xd2\xa5float\xcb@(\xae\x14z\xe1G\xae\xa3bin\xc4\x041234\xa9realworld\xac\xc7\x8b\xef\xbf\xbd\xed\xa1\x82\xef\xbf\xbd\x12\xabalmostlarge\xcf\xff\xff\xff\xff\xff\xff\xff\xfe\xb1extlargeThreshold\xcf\xff\xff\xff\xff\xff\xff\xff\xff\xa8extlarge\xc7\t\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\xabalmostsmall\xd3\x80\x00\x00\x00\x00\x00\x00\x01\xb4almostsmallThreshold\xd3\x80\x00\x00\x00\x00\x00\x00\x00\xa8extsmall\xc7\t\x01\xff\x7f\xff\xff\xff\xff\xff\xff\xff'
111
155
  struct = (
112
156
  'hehe',
113
157
  {
@@ -115,7 +159,15 @@ class MsgPackTest(s_t_utils.SynTest):
115
159
  'int': 1234,
116
160
  'float': 12.34,
117
161
  'bin': b'1234',
118
- 'realworld': '\u01cb\ufffd\ud842\ufffd\u0012'
162
+ 'realworld': '\u01cb\ufffd\ud842\ufffd\u0012',
163
+ 'almostlarge': 0xffffffffffffffff - 1,
164
+ 'extlargeThreshold': 0xffffffffffffffff,
165
+ # extlarge is handled with our custom extension type
166
+ 'extlarge': 0xffffffffffffffff + 1,
167
+ 'almostsmall': -0x8000000000000000 + 1,
168
+ 'almostsmallThreshold': -0x8000000000000000,
169
+ # extsmall is handled with our custom extension type
170
+ 'extsmall': -0x8000000000000000 - 1,
119
171
  }
120
172
  )
121
173
  unode = s_msgpack.un(buf)
@@ -135,7 +187,7 @@ class MsgPackTest(s_t_utils.SynTest):
135
187
  unpk = s_msgpack.Unpk()
136
188
  objs = unpk.feed(buf)
137
189
  self.len(1, objs)
138
- self.eq(objs[0], (71, struct))
190
+ self.eq(objs[0], (212, struct))
139
191
 
140
192
  # Generic isok helper
141
193
  self.true(s_msgpack.isok(1))
@@ -148,6 +200,8 @@ class MsgPackTest(s_t_utils.SynTest):
148
200
  self.true(s_msgpack.isok([1]))
149
201
  self.true(s_msgpack.isok((1,)))
150
202
  self.true(s_msgpack.isok({1: 1}))
203
+ self.true(s_msgpack.isok(0xffffffffffffffff + 1))
204
+ self.true(s_msgpack.isok(-0x8000000000000000 - 1))
151
205
  # unpackage types
152
206
  self.false(s_msgpack.isok({1, 2})) # set
153
207
  self.false(s_msgpack.isok(print)) # function
@@ -198,10 +252,6 @@ class MsgPackTest(s_t_utils.SynTest):
198
252
  self.raises(s_exc.NotMsgpackSafe, enfunc, {1, 2})
199
253
  self.raises(s_exc.NotMsgpackSafe, enfunc, Exception())
200
254
  self.raises(s_exc.NotMsgpackSafe, enfunc, s_msgpack.en)
201
- # too long
202
- with self.raises(s_exc.NotMsgpackSafe) as cm:
203
- enfunc({'longlong': 45234928034723904723906})
204
- self.isin('OverflowError', cm.exception.get('mesg'))
205
255
 
206
256
  def test_msgpack_bad_types(self):
207
257
  self.checkBadTypes(s_msgpack.en)
@@ -3,7 +3,6 @@ from unittest import mock
3
3
 
4
4
  import synapse.exc as s_exc
5
5
  import synapse.common as s_common
6
- import synapse.cortex as s_cortex
7
6
 
8
7
  import synapse.lib.cell as s_cell
9
8
  import synapse.lib.nexus as s_nexus
@@ -302,3 +301,47 @@ class NexusTest(s_t_utils.SynTest):
302
301
  with self.raises(s_exc.IsReadOnly):
303
302
  await cell01.sync()
304
303
  self.isin(s_nexus.leaderversion, cell01.nexsroot.writeholds)
304
+
305
+ async def test_mirror_nexus_loop_failure(self):
306
+ with self.getTestDir() as dirn:
307
+
308
+ s_common.yamlsave({'nexslog:en': True}, dirn, 'cell.yaml')
309
+ async with await s_cell.Cell.anit(dirn=dirn) as cell00:
310
+
311
+ await cell00.runBackup(name='cell01')
312
+
313
+ path = s_common.genpath(dirn, 'backups', 'cell01')
314
+
315
+ conf = s_common.yamlload(path, 'cell.yaml')
316
+ conf['mirror'] = f'cell://{dirn}'
317
+ s_common.yamlsave(conf, path, 'cell.yaml')
318
+
319
+ seen = False
320
+ restarted = False
321
+ orig = s_nexus.NexsRoot.delWriteHold
322
+
323
+ # Patch NexsRoot.delWriteHold so we can cause an exception in
324
+ # the setup part of the nexus loop (NexsRoot.runMirrorLoop). The
325
+ # exception should only happen one time so we can check that the
326
+ # proxy and the nexus loop were both restarted
327
+ async def delWriteHold(self, reason):
328
+ nonlocal seen
329
+ nonlocal restarted
330
+ if not seen:
331
+ seen = True
332
+ raise Exception('Knock over the nexus setup.')
333
+
334
+ restarted = True
335
+ return await orig(self, reason)
336
+
337
+ with mock.patch('synapse.lib.nexus.NexsRoot.delWriteHold', delWriteHold):
338
+ with self.getLoggerStream('synapse.lib.nexus') as stream:
339
+ async with await s_cell.Cell.anit(dirn=path) as cell01:
340
+ await cell01.sync()
341
+
342
+ stream.seek(0)
343
+ data = stream.read()
344
+ mesg = 'Unknown error during mirror loop startup: Knock over the nexus setup.'
345
+ self.isin(mesg, data)
346
+
347
+ self.true(restarted)