synapse 2.209.0__py311-none-any.whl → 2.211.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 (68) hide show
  1. synapse/lib/base.py +2 -3
  2. synapse/lib/cell.py +14 -4
  3. synapse/lib/coro.py +5 -0
  4. synapse/lib/json.py +48 -22
  5. synapse/lib/nexus.py +6 -0
  6. synapse/lib/node.py +1 -1
  7. synapse/lib/schemas.py +10 -1
  8. synapse/lib/share.py +0 -3
  9. synapse/lib/snap.py +3 -0
  10. synapse/lib/storm.py +10 -2
  11. synapse/lib/stormlib/auth.py +6 -0
  12. synapse/lib/stormlib/notifications.py +12 -2
  13. synapse/lib/stormtypes.py +2 -2
  14. synapse/lib/version.py +2 -2
  15. synapse/models/entity.py +26 -0
  16. synapse/models/inet.py +55 -0
  17. synapse/models/orgs.py +0 -1
  18. synapse/models/person.py +9 -2
  19. synapse/tests/test_cortex.py +3 -3
  20. synapse/tests/test_lib_aha.py +1 -1
  21. synapse/tests/test_lib_base.py +5 -0
  22. synapse/tests/test_lib_cell.py +28 -8
  23. synapse/tests/test_lib_coro.py +23 -0
  24. synapse/tests/test_lib_json.py +41 -16
  25. synapse/tests/test_lib_storm.py +20 -0
  26. synapse/tests/test_lib_stormtypes.py +16 -18
  27. synapse/tests/test_lib_view.py +14 -0
  28. synapse/tests/test_model_entity.py +21 -0
  29. synapse/tests/test_model_gov_intl.py +2 -2
  30. synapse/tests/test_model_inet.py +30 -0
  31. synapse/tests/test_model_media.py +1 -0
  32. synapse/tests/test_model_orgs.py +18 -2
  33. synapse/tests/test_model_person.py +12 -0
  34. synapse/tests/test_telepath.py +52 -41
  35. synapse/tests/test_tools_aha.py +7 -8
  36. synapse/tests/test_tools_genpkg.py +9 -0
  37. synapse/tests/utils.py +1 -1
  38. synapse/tools/aha/clone.py +7 -1
  39. synapse/tools/aha/easycert.py +37 -42
  40. synapse/tools/aha/enroll.py +7 -1
  41. synapse/tools/aha/list.py +60 -65
  42. synapse/tools/aha/mirror.py +7 -1
  43. synapse/tools/aha/provision/service.py +7 -1
  44. synapse/tools/aha/provision/user.py +7 -1
  45. synapse/tools/apikey.py +8 -1
  46. synapse/tools/autodoc.py +8 -2
  47. synapse/tools/axon2axon.py +7 -1
  48. synapse/tools/cellauth.py +6 -5
  49. synapse/tools/cmdr.py +2 -1
  50. synapse/tools/csvtool.py +7 -2
  51. synapse/tools/feed.py +8 -2
  52. synapse/tools/genpkg.py +16 -3
  53. synapse/tools/healthcheck.py +7 -1
  54. synapse/tools/livebackup.py +7 -3
  55. synapse/tools/modrole.py +7 -1
  56. synapse/tools/moduser.py +7 -2
  57. synapse/tools/promote.py +7 -3
  58. synapse/tools/pullfile.py +6 -1
  59. synapse/tools/pushfile.py +7 -1
  60. synapse/tools/reload.py +7 -4
  61. synapse/tools/rstorm.py +8 -2
  62. synapse/tools/snapshot.py +7 -1
  63. synapse/tools/storm.py +7 -1
  64. {synapse-2.209.0.dist-info → synapse-2.211.0.dist-info}/METADATA +2 -2
  65. {synapse-2.209.0.dist-info → synapse-2.211.0.dist-info}/RECORD +68 -67
  66. {synapse-2.209.0.dist-info → synapse-2.211.0.dist-info}/WHEEL +1 -1
  67. {synapse-2.209.0.dist-info → synapse-2.211.0.dist-info}/licenses/LICENSE +0 -0
  68. {synapse-2.209.0.dist-info → synapse-2.211.0.dist-info}/top_level.txt +0 -0
@@ -781,7 +781,7 @@ class AhaTest(s_test.SynTest):
781
781
  self.false(axon3.isactive)
782
782
  self.eq('aha://root@axon...', axon03.conf.get('mirror'))
783
783
 
784
- retn, outp = await self.execToolMain(s_a_list._main, [aha.getLocalUrl()])
784
+ retn, outp = await self.execToolMain(s_a_list.main, [aha.getLocalUrl()])
785
785
  self.eq(retn, 0)
786
786
  outp.expect('Service network leader')
787
787
  outp.expect('00.axon synapse True')
@@ -86,6 +86,11 @@ class BaseTest(s_t_utils.SynTest):
86
86
  await Hehe.anit(-1)
87
87
  self.eq(cm.exception.get('mesg'), 'boom')
88
88
 
89
+ if __debug__:
90
+ with self.raises(AssertionError) as cm:
91
+ Hehe()
92
+ self.eq(str(cm.exception), 'Objects from Base must be constructed solely via "anit"')
93
+
89
94
  async def test_coro_fini(self):
90
95
 
91
96
  event = asyncio.Event()
@@ -2238,7 +2238,7 @@ class CellTest(s_t_utils.SynTest):
2238
2238
  self.false(core00.isactive)
2239
2239
 
2240
2240
  modinfo = s_common.yamlload(core00.dirn, 'cell.mods.yaml')
2241
- self.isin('01.core', modinfo.get('mirror', ''))
2241
+ self.eq('aha://root@core...', modinfo.get('mirror', ''))
2242
2242
  modinfo = s_common.yamlload(core01.dirn, 'cell.mods.yaml')
2243
2243
  self.none(modinfo.get('mirror'))
2244
2244
 
@@ -2321,7 +2321,7 @@ class CellTest(s_t_utils.SynTest):
2321
2321
  self.false(core00.isactive)
2322
2322
 
2323
2323
  modinfo = s_common.yamlload(core00.dirn, 'cell.mods.yaml')
2324
- self.isin('01.core', modinfo.get('mirror', ''))
2324
+ self.eq('aha://root@core...', modinfo.get('mirror', ''))
2325
2325
  modinfo = s_common.yamlload(core01.dirn, 'cell.mods.yaml')
2326
2326
  self.none(modinfo.get('mirror'))
2327
2327
 
@@ -2333,7 +2333,7 @@ class CellTest(s_t_utils.SynTest):
2333
2333
  modinfo = s_common.yamlload(core00.dirn, 'cell.mods.yaml')
2334
2334
  self.none(modinfo.get('mirror'))
2335
2335
  modinfo = s_common.yamlload(core01.dirn, 'cell.mods.yaml')
2336
- self.isin('00.core', modinfo.get('mirror', ''))
2336
+ self.eq('aha://root@core...', modinfo.get('mirror', ''))
2337
2337
 
2338
2338
  # Backup the mirror (core01) which points to the core00
2339
2339
  async with await axon00.upload() as upfd:
@@ -3285,6 +3285,7 @@ class CellTest(s_t_utils.SynTest):
3285
3285
  dirn00 = s_common.genpath(dirn, '00.cell')
3286
3286
  dirn01 = s_common.genpath(dirn, '01.cell')
3287
3287
  dirn02 = s_common.genpath(dirn, '02.cell')
3288
+ dirn0002 = s_common.genpath(dirn, '00.02.cell')
3288
3289
 
3289
3290
  cell00 = await base.enter_context(self.addSvcToAha(aha, '00.cell', s_cell.Cell, dirn=dirn00))
3290
3291
  cell01 = await base.enter_context(self.addSvcToAha(aha, '01.cell', s_cell.Cell, dirn=dirn01,
@@ -3301,24 +3302,43 @@ class CellTest(s_t_utils.SynTest):
3301
3302
  await cell01.handoff('some://url')
3302
3303
  self.isin('01.cell is not the current leader', cm.exception.get('mesg'))
3303
3304
 
3304
- # Note: The following behavior may change when SYN-7659 is addressed and greater
3305
- # control over the topology update is available during the promotion process.
3306
- # Promote 02.cell -> Promote 01.cell -> Promote 00.cell -> BadState exception
3305
+ # Promote 02.cell -> Promote 01.cell -> Promote 00.cell, without breaking the configured topology
3307
3306
  await cell02.promote(graceful=True)
3308
3307
  self.false(cell00.isactive)
3308
+ self.eq(cell00.conf.get('mirror'), 'aha://root@cell...')
3309
3309
  self.false(cell01.isactive)
3310
+ self.eq(cell01.conf.get('mirror'), 'aha://root@cell...')
3310
3311
  self.true(cell02.isactive)
3312
+ self.none(cell02.conf.get('mirror'))
3311
3313
  await cell02.sync()
3312
3314
 
3313
3315
  await cell01.promote(graceful=True)
3314
3316
  self.false(cell00.isactive)
3317
+ self.eq(cell00.conf.get('mirror'), 'aha://root@cell...')
3315
3318
  self.true(cell01.isactive)
3319
+ self.none(cell01.conf.get('mirror'))
3316
3320
  self.false(cell02.isactive)
3321
+ self.eq(cell02.conf.get('mirror'), 'aha://root@cell...')
3317
3322
  await cell02.sync()
3318
3323
 
3324
+ await cell00.promote(graceful=True)
3325
+ self.true(cell00.isactive)
3326
+ self.none(cell00.conf.get('mirror'))
3327
+ self.false(cell01.isactive)
3328
+ self.eq(cell01.conf.get('mirror'), 'aha://root@cell...')
3329
+ self.false(cell02.isactive)
3330
+ self.eq(cell02.conf.get('mirror'), 'aha://root@cell...')
3331
+ await cell02.sync()
3332
+
3333
+ # A follower of a follower cannot be promoted up since its leader is not the active cell.
3334
+ cell0002 = await base.enter_context(self.addSvcToAha(aha, '00.02.cell', s_cell.Cell, dirn=dirn0002,
3335
+ provinfo={'mirror': '02.cell'}))
3336
+ self.false(cell0002.isactive)
3337
+ self.eq(cell0002.conf.get('mirror'), 'aha://root@02.cell...')
3319
3338
  with self.raises(s_exc.BadState) as cm:
3320
- await cell00.promote(graceful=True)
3321
- self.isin('02.cell is not the current leader', cm.exception.get('mesg'))
3339
+ await cell0002.promote(graceful=True)
3340
+ mesg = 'ahaname=02.cell is not the current leader and cannot handoff leadership to aha://00.02.cell.synapse.'
3341
+ self.isin(mesg, cm.exception.get('mesg'))
3322
3342
 
3323
3343
  async def test_cell_get_aha_proxy(self):
3324
3344
 
@@ -212,3 +212,26 @@ class CoroTest(s_t_utils.SynTest):
212
212
 
213
213
  with self.raises(Exception):
214
214
  await s_coro._parserforked(newp)
215
+
216
+ async def test_lib_coro_create_task(self):
217
+
218
+ async def sleep(n):
219
+ await asyncio.sleep(n)
220
+ if n == 0:
221
+ return 1 / 0
222
+ return n
223
+
224
+ s_coro.create_task(sleep(0.1))
225
+ s_coro.create_task(sleep(0.15))
226
+ s_coro.create_task(sleep(0.2))
227
+ self.len(3, s_coro.bgtasks)
228
+ results = await s_coro.await_bg_tasks()
229
+ self.eq(set(results), {0.1, 0.15, 0.2})
230
+ self.len(0, s_coro.bgtasks)
231
+ results = await s_coro.await_bg_tasks()
232
+ self.eq(results, [])
233
+
234
+ s_coro.create_task(sleep(0))
235
+ results = await s_coro.await_bg_tasks()
236
+ self.len(1, results)
237
+ self.isinstance(results[0], ZeroDivisionError)
@@ -1,7 +1,7 @@
1
1
  import io
2
2
  import json
3
3
 
4
- import orjson
4
+ import yyjson
5
5
 
6
6
  import synapse.exc as s_exc
7
7
  import synapse.common as s_common
@@ -44,9 +44,9 @@ class JsonTest(s_test.SynTest):
44
44
  inval = '{"a": "😀\ud83d\ude47"}'
45
45
  outval = {'a': '😀\ud83d\ude47'}
46
46
 
47
- # orjson.dumps fails because of the surrogate pairs
48
- with self.raises(orjson.JSONDecodeError):
49
- orjson.loads(inval)
47
+ # yyjson.loads fails because of the surrogate pairs
48
+ with self.raises(ValueError):
49
+ yyjson.loads(inval)
50
50
 
51
51
  # stdlib json.loads passes because of voodoo magic
52
52
  self.eq(outval, json.loads(inval))
@@ -63,9 +63,9 @@ class JsonTest(s_test.SynTest):
63
63
  inval = {'a': '😀\ud83d\ude47'}
64
64
  outval = b'{"a": "\\ud83d\\ude00\\ud83d\\ude47"}'
65
65
 
66
- # orjson.dumps fails because of the surrogate pairs
67
- with self.raises(TypeError):
68
- orjson.dumps(inval)
66
+ # yyjson.dumps fails because of the surrogate pairs
67
+ with self.raises(UnicodeEncodeError):
68
+ yyjson.dumps(inval)
69
69
 
70
70
  # stdlib json.dumps passes because of voodoo magic
71
71
  self.eq(outval.decode(), json.dumps(inval))
@@ -85,15 +85,15 @@ class JsonTest(s_test.SynTest):
85
85
 
86
86
  with self.raises(s_exc.MustBeJsonSafe) as exc:
87
87
  s_json.dumps({}.items())
88
- self.eq(exc.exception.get('mesg'), 'Type is not JSON serializable: dict_items')
88
+ self.eq(exc.exception.get('mesg'), "TypeError: Object of type 'dict_items' is not JSON serializable")
89
89
 
90
90
  with self.raises(s_exc.MustBeJsonSafe) as exc:
91
91
  s_json.dumps({1: 'foo'})
92
- self.eq(exc.exception.get('mesg'), 'Dict key must be str')
92
+ self.eq(exc.exception.get('mesg'), "TypeError: Dictionary keys must be strings")
93
93
 
94
94
  with self.raises(s_exc.MustBeJsonSafe) as exc:
95
95
  s_json.dumps({'\ud83d\ude47': {}.items()})
96
- self.eq(exc.exception.get('mesg'), 'Object of type dict_items is not JSON serializable')
96
+ self.eq(exc.exception.get('mesg'), "TypeError: Dictionary keys must be strings")
97
97
 
98
98
  self.eq(b'"dict_items([])"', s_json.dumps({}.items(), default=str))
99
99
 
@@ -111,6 +111,20 @@ class JsonTest(s_test.SynTest):
111
111
  s_json.dump({'c': 'd', 'a': 'b'}, buf)
112
112
  self.eq(b'{"c":"d","a":"b"}', buf.getvalue())
113
113
 
114
+ async def test_lib_json_large_integers(self):
115
+ valu = [
116
+ 1, 2,
117
+ -1, -2,
118
+ 1.0, 2.0,
119
+ -1.0, -2.0,
120
+ 2**63, -2**63, -2**63 - 1,
121
+ 2**64, -2**64, 2**64 + 1,
122
+ 2**128, 2**128 + 1,
123
+ -2**128, -2**128 - 1,
124
+ ]
125
+
126
+ self.eq(valu, s_json.loads(s_json.dumps(valu)))
127
+
114
128
  async def test_jsload(self):
115
129
  with self.getTestDir() as dirn:
116
130
  with s_common.genfile(dirn, 'jsload.json') as fp:
@@ -147,41 +161,52 @@ class JsonTest(s_test.SynTest):
147
161
 
148
162
  buf = io.BytesIO()
149
163
 
164
+ msg = "Object of type '_io.BytesIO' is not JSON serializable"
150
165
  with self.raises(s_exc.MustBeJsonSafe) as exc:
151
166
  s_json.reqjsonsafe(buf)
152
- self.isin('Type is not JSON serializable: _io.BytesIO', exc.exception.get('mesg'))
167
+ self.isin(msg, exc.exception.get('mesg'))
153
168
 
154
169
  with self.raises(s_exc.MustBeJsonSafe) as exc:
155
170
  s_json.reqjsonsafe({'foo': buf})
156
- self.isin('Type is not JSON serializable: _io.BytesIO', exc.exception.get('mesg'))
171
+ self.isin(msg, exc.exception.get('mesg'))
157
172
 
158
173
  with self.raises(s_exc.MustBeJsonSafe) as exc:
159
174
  s_json.reqjsonsafe(['foo', buf])
160
- self.isin('Type is not JSON serializable: _io.BytesIO', exc.exception.get('mesg'))
175
+ self.isin(msg, exc.exception.get('mesg'))
161
176
 
162
177
  items = (
163
178
  (None, None),
164
179
  (1234, None),
165
180
  ('1234', None),
181
+ ('1234"', None),
166
182
  ({'asdf': 'haha'}, None),
167
183
  ({'a': (1,), 'b': [{'': 4}, 56, None, {'t': True, 'f': False}, 'oh my']}, None),
168
184
  (b'1234', s_exc.MustBeJsonSafe),
185
+ (b'1234"', s_exc.MustBeJsonSafe),
186
+ ({'a': float('nan')}, s_exc.MustBeJsonSafe),
169
187
  ({'a': 'a', 2: 2}, s_exc.MustBeJsonSafe),
170
188
  ({'a', 'b', 'c'}, s_exc.MustBeJsonSafe),
171
189
  (s_common.novalu, s_exc.MustBeJsonSafe),
172
190
  )
173
191
  for (item, eret) in items:
174
192
  if eret is None:
175
- self.none(s_json.reqjsonsafe(item))
193
+ self.none(s_json.reqjsonsafe(item), msg=item)
176
194
  else:
177
195
  with self.raises(eret):
178
196
  s_json.reqjsonsafe(item)
179
197
 
180
- text = '😀\ud83d\ude47'
198
+ for text in ['😀', 'asdf', 'asdf"', '"asdf']:
199
+ s_json.reqjsonsafe(text)
200
+
201
+ text = ['😀\ud83d\ude47']
181
202
  s_json.reqjsonsafe(text)
182
203
  with self.raises(s_exc.MustBeJsonSafe) as exc:
183
204
  s_json.reqjsonsafe(text, strict=True)
184
- self.eq(exc.exception.get('mesg'), 'str is not valid UTF-8: surrogates not allowed')
205
+ self.eq(exc.exception.get('mesg'), "'utf-8' codec can't encode characters in position 1-2: surrogates not allowed")
206
+
207
+ with self.raises(s_exc.MustBeJsonSafe) as exc:
208
+ s_json.reqjsonsafe(b'1234', strict=True)
209
+ self.eq(exc.exception.get('mesg'), 'Object of type bytes is not JSON serializable')
185
210
 
186
211
  async def test_lib_json_data_at_rest(self):
187
212
  async with self.getRegrCore('json-data') as core:
@@ -2384,6 +2384,26 @@ class StormTest(s_t_utils.SynTest):
2384
2384
  self.eq('barvuln', embeds['vuln']['name'])
2385
2385
  self.eq('foohw', embeds['node']['name'])
2386
2386
 
2387
+ # embed through `econ:pay:instrument` type that extends from `ndef`
2388
+ await core.nodes('''
2389
+ [ econ:acct:payment=* :from:instrument={ [ econ:pay:card=(testcard,) :name=infime ] } ]
2390
+ ''')
2391
+
2392
+ opts = {
2393
+ 'embeds': {
2394
+ 'econ:acct:payment': {
2395
+ 'from:instrument': ['name'],
2396
+ }
2397
+ }
2398
+ }
2399
+ msgs = await core.stormlist('econ:acct:payment', opts=opts)
2400
+ node = [m[1] for m in msgs if m[0] == 'node'][0]
2401
+ self.eq('econ:acct:payment', node[0][0])
2402
+
2403
+ embeds = node[1]['embeds']
2404
+ self.eq('86caf7a47348d56b2f6bec3e767a9fc7eaaaf5a80d7bbaa235fab763c7dcc560', embeds['from:instrument']['*'])
2405
+ self.eq('infime', embeds['from:instrument']['name'])
2406
+
2387
2407
  async def test_storm_wget(self):
2388
2408
 
2389
2409
  async def _getRespFromSha(core, mesgs):
@@ -1291,35 +1291,33 @@ class StormTypesTest(s_test.SynTest):
1291
1291
  self.len(1, nodes)
1292
1292
  self.eq('visi@vertex.link', nodes[0].ndef[1])
1293
1293
 
1294
- nodes = await core.nodes('$s = woot [ test:int=$s.startswith(w) ]')
1295
- self.eq(1, nodes[0].ndef[1])
1294
+ self.true(await core.callStorm('$s = woot return($s.startswith(w))'))
1296
1295
 
1297
- nodes = await core.nodes('$s = woot [ test:int=$s.endswith(visi) ]')
1298
- self.eq(0, nodes[0].ndef[1])
1296
+ self.false(await core.callStorm('$s = woot return($s.endswith(visi))'))
1299
1297
 
1300
- nodes = await core.nodes('$s = woot [ test:str=$s.rjust(10) ]')
1301
- self.eq(' woot', nodes[0].ndef[1])
1298
+ valu = await core.callStorm('$s = woot return($s.rjust(10))')
1299
+ self.eq(' woot', valu)
1302
1300
 
1303
- nodes = await core.nodes('$s = woot [ test:str=$s.rjust(10, x) ]')
1304
- self.eq('xxxxxxwoot', nodes[0].ndef[1])
1301
+ valu = await core.callStorm('$s = woot return($s.rjust(10, x))')
1302
+ self.eq('xxxxxxwoot', valu)
1305
1303
 
1306
- nodes = await core.nodes('$s = woot [ test:str=$s.ljust(10) ]')
1307
- self.eq('woot ', nodes[0].ndef[1])
1304
+ valu = await core.callStorm('$s = woot return($s.ljust(10))')
1305
+ self.eq('woot ', valu)
1308
1306
 
1309
- nodes = await core.nodes('$s = woot [ test:str=$s.ljust(10, x) ]')
1310
- self.eq('wootxxxxxx', nodes[0].ndef[1])
1307
+ valu = await core.callStorm('$s = woot return($s.ljust(10, x))')
1308
+ self.eq('wootxxxxxx', valu)
1311
1309
 
1312
1310
  sobj = s_stormtypes.Str('beepbeep')
1313
1311
  self.len(8, sobj)
1314
1312
 
1315
- nodes = await core.nodes("$s = (foo, bar, baz) [ test:str=('.').join($s) ]")
1316
- self.eq('foo.bar.baz', nodes[0].ndef[1])
1313
+ valu = await core.callStorm('$s = (foo, bar, baz) return((".").join($s))')
1314
+ self.eq('foo.bar.baz', valu)
1317
1315
 
1318
- nodes = await core.nodes('$s = foo-bar-baz [ test:str=$s.replace("-", ".") ]')
1319
- self.eq('foo.bar.baz', nodes[0].ndef[1])
1316
+ valu = await core.callStorm('$s = foo-bar-baz return($s.replace("-", "."))')
1317
+ self.eq('foo.bar.baz', valu)
1320
1318
 
1321
- nodes = await core.nodes('$s = foo-bar-baz [ test:str=$s.replace("-", ".", 1) ]')
1322
- self.eq('foo.bar-baz', nodes[0].ndef[1])
1319
+ valu = await core.callStorm('$s = foo-bar-baz return($s.replace("-", ".", 1))')
1320
+ self.eq('foo.bar-baz', valu)
1323
1321
 
1324
1322
  q = '$foo=" foo " return ( $foo.strip() )'
1325
1323
  self.eq('foo', await core.callStorm(q))
@@ -332,6 +332,20 @@ class ViewTest(s_t_utils.SynTest):
332
332
  self.len(100, nodeedit[0][2])
333
333
  self.len(2, nodeedit[1][2])
334
334
 
335
+ await core.nodes('[ test:str=lowertag +#a.b=2020]')
336
+
337
+ vdef2 = await core.view.fork()
338
+ opts = {'view': vdef2['iden']}
339
+ await core.nodes('test:str=lowertag [ +#a.b.c ]', opts=opts)
340
+
341
+ retn = await core.callStorm('test:str=lowertag return($node.getStorNodes())', opts=opts)
342
+
343
+ # Only leaf tag is added in our top layer
344
+ self.isin('a.b.c', retn[0].get('tags'))
345
+ self.notin('a.b', retn[0].get('tags'))
346
+
347
+ self.isin('a.b', retn[1].get('tags'))
348
+
335
349
  async def test_view_merge_ival(self):
336
350
 
337
351
  async with self.getTestCore() as core:
@@ -0,0 +1,21 @@
1
+ import synapse.tests.utils as s_test
2
+
3
+ class EntityModelTest(s_test.SynTest):
4
+
5
+ async def test_entity_relationship(self):
6
+
7
+ async with self.getTestCore() as core:
8
+
9
+ nodes = await core.nodes('''[
10
+ entity:relationship=*
11
+ :type=tasks
12
+ :period=(2022, ?)
13
+ :source={[ ou:org=({"name": "China Ministry of State Security (MSS)"}) ]}
14
+ :target={[ risk:threat=({"org:name": "APT34", "reporter:name": "vertex"}) ]}
15
+ ]''')
16
+
17
+ self.len(1, nodes)
18
+ self.eq(nodes[0].get('type'), 'tasks.')
19
+ self.eq(nodes[0].get('period'), (1640995200000, 9223372036854775807))
20
+ self.eq(nodes[0].get('source'), ('ou:org', '3332a704ed21dc3274d5731acc54a0ee'))
21
+ self.eq(nodes[0].get('target'), ('risk:threat', 'e15738ebae52273300b51c08eaad3a36'))
@@ -1,6 +1,6 @@
1
- from synapse.tests.utils import SynTest
1
+ import synapse.tests.utils as s_t_utils
2
2
 
3
- class IntlGovTest(SynTest):
3
+ class IntlGovTest(s_t_utils.SynTest):
4
4
 
5
5
  async def test_models_intl(self):
6
6
 
@@ -2971,7 +2971,9 @@ class InetModelTest(s_t_utils.SynTest):
2971
2971
  q = '''
2972
2972
  [ inet:service:platform=(slack,)
2973
2973
  :url="https://slack.com"
2974
+ :urls=(https://slacker.com,)
2974
2975
  :name=Slack
2976
+ :names=("slack chat",)
2975
2977
  :provider={ ou:org:name=$provname }
2976
2978
  :provider:name=$provname
2977
2979
  ]
@@ -2980,11 +2982,19 @@ class InetModelTest(s_t_utils.SynTest):
2980
2982
  self.len(1, nodes)
2981
2983
  self.eq(nodes[0].ndef, ('inet:service:platform', s_common.guid(('slack',))))
2982
2984
  self.eq(nodes[0].get('url'), 'https://slack.com')
2985
+ self.eq(nodes[0].get('urls'), ('https://slacker.com',))
2983
2986
  self.eq(nodes[0].get('name'), 'slack')
2987
+ self.eq(nodes[0].get('names'), ('slack chat',))
2984
2988
  self.eq(nodes[0].get('provider'), provider.ndef[1])
2985
2989
  self.eq(nodes[0].get('provider:name'), provname.lower())
2986
2990
  platform = nodes[0]
2987
2991
 
2992
+ nodes = await core.nodes('[ inet:service:platform=({"name": "slack chat"}) ]')
2993
+ self.eq(nodes[0].ndef, platform.ndef)
2994
+
2995
+ nodes = await core.nodes('[ inet:service:platform=({"url": "https://slacker.com"}) ]')
2996
+ self.eq(nodes[0].ndef, platform.ndef)
2997
+
2988
2998
  q = '''
2989
2999
  [ inet:service:instance=(vertex, slack)
2990
3000
  :id='T2XK1223Y'
@@ -3164,6 +3174,7 @@ class InetModelTest(s_t_utils.SynTest):
3164
3174
  :creator=$visiiden
3165
3175
  :platform=$platiden
3166
3176
  :instance=$instiden
3177
+ :topic=' My Topic '
3167
3178
  ]
3168
3179
  '''
3169
3180
  opts = {'vars': {
@@ -3175,6 +3186,7 @@ class InetModelTest(s_t_utils.SynTest):
3175
3186
  self.len(1, nodes)
3176
3187
  self.eq(nodes[0].ndef, ('inet:service:channel', s_common.guid(('general', 'channel', 'vertex', 'slack'))))
3177
3188
  self.eq(nodes[0].get('name'), 'general')
3189
+ self.eq(nodes[0].get('topic'), 'my topic')
3178
3190
  self.eq(nodes[0].get('period'), (1420070400000, 9223372036854775807))
3179
3191
  self.eq(nodes[0].get('creator'), visiacct.ndef[1])
3180
3192
  self.eq(nodes[0].get('platform'), platform.ndef[1])
@@ -3243,12 +3255,18 @@ class InetModelTest(s_t_utils.SynTest):
3243
3255
  :group=$devsiden
3244
3256
  :public=$lib.false
3245
3257
  :repost=*
3258
+ :mentions=(
3259
+ (inet:service:group, $devsiden),
3260
+ (inet:service:account, $blckiden),
3261
+ (inet:service:account, $blckiden),
3262
+ )
3246
3263
  )
3247
3264
 
3248
3265
  (inet:service:message=(blackout, visi, 1715856900000, vertex, slack)
3249
3266
  :type=chat.direct
3250
3267
  :to=$visiiden
3251
3268
  :public=$lib.false
3269
+ :mentions?=((inet:service:message:attachment, $atchiden),)
3252
3270
  )
3253
3271
 
3254
3272
  (inet:service:message=(blackout, general, 1715856900000, vertex, slack)
@@ -3296,10 +3314,15 @@ class InetModelTest(s_t_utils.SynTest):
3296
3314
  self.eq(nodes[0].get('group'), devsgrp.ndef[1])
3297
3315
  self.false(nodes[0].get('public'))
3298
3316
  self.eq(nodes[0].get('type'), 'chat.group.')
3317
+ self.eq(
3318
+ nodes[0].get('mentions'),
3319
+ (('inet:service:account', blckacct.ndef[1]), ('inet:service:group', devsgrp.ndef[1]))
3320
+ )
3299
3321
 
3300
3322
  self.eq(nodes[1].get('to'), visiacct.ndef[1])
3301
3323
  self.false(nodes[1].get('public'))
3302
3324
  self.eq(nodes[1].get('type'), 'chat.direct.')
3325
+ self.none(nodes[1].get('mentions'))
3303
3326
 
3304
3327
  self.eq(nodes[2].get('channel'), gnrlchan.ndef[1])
3305
3328
  self.true(nodes[2].get('public'))
@@ -3370,12 +3393,15 @@ class InetModelTest(s_t_utils.SynTest):
3370
3393
 
3371
3394
  q = '''
3372
3395
  [ inet:service:access=(api, blackout, 1715856900000, vertex, slack)
3396
+ :action=foo.bar
3373
3397
  :account=$blckiden
3374
3398
  :instance=$instiden
3375
3399
  :platform=$platiden
3376
3400
  :resource=$rsrciden
3377
3401
  :success=$lib.true
3378
3402
  :time=(1715856900000)
3403
+ :app={[ inet:service:app=({"name": "slack web"}) ]}
3404
+ :client:app={[ inet:service:app=({"name": "slack web"}) :desc="The slack web application"]}
3379
3405
  ]
3380
3406
  '''
3381
3407
  opts = {'vars': {
@@ -3387,16 +3413,19 @@ class InetModelTest(s_t_utils.SynTest):
3387
3413
  }}
3388
3414
  nodes = await core.nodes(q, opts=opts)
3389
3415
  self.len(1, nodes)
3416
+ self.eq(nodes[0].get('action'), 'foo.bar.')
3390
3417
  self.eq(nodes[0].get('account'), blckacct.ndef[1])
3391
3418
  self.eq(nodes[0].get('instance'), platinst.ndef[1])
3392
3419
  self.eq(nodes[0].get('platform'), platform.ndef[1])
3393
3420
  self.eq(nodes[0].get('resource'), resource.ndef[1])
3394
3421
  self.true(nodes[0].get('success'))
3395
3422
  self.eq(nodes[0].get('time'), 1715856900000)
3423
+ self.eq(nodes[0].get('app'), nodes[0].get('client:app'))
3396
3424
 
3397
3425
  q = '''
3398
3426
  [ inet:service:message=(visi, says, relax)
3399
3427
  :title="Hehe Haha"
3428
+ :hashtags="#hehe,#haha,#hehe"
3400
3429
  :thread={[
3401
3430
  inet:service:thread=*
3402
3431
  :title="Woot Woot"
@@ -3410,6 +3439,7 @@ class InetModelTest(s_t_utils.SynTest):
3410
3439
  '''
3411
3440
  nodes = await core.nodes(q)
3412
3441
  self.len(1, nodes)
3442
+ self.eq(['#haha', '#hehe'], nodes[0].get('hashtags'))
3413
3443
  self.len(1, await core.nodes('inet:service:message=(visi, says, hello) -> inet:service:thread:message'))
3414
3444
  self.len(1, await core.nodes('''
3415
3445
  inet:service:message:title="hehe haha"
@@ -1,5 +1,6 @@
1
1
  import logging
2
2
 
3
+ import synapse.exc as s_exc
3
4
  import synapse.common as s_common
4
5
 
5
6
  import synapse.tests.utils as s_t_utils
@@ -455,8 +455,24 @@ class OuModelTest(s_t_utils.SynTest):
455
455
  'place': place0,
456
456
  'url': 'http://arrowcon.org/2018/dinner',
457
457
  }
458
- q = '''[(ou:conference:event=$valu :name=$p.name :desc=$p.desc :start=$p.start :end=$p.end
459
- :conference=$p.conference :contact=$p.contact :place=$p.place :url=$p.url)]'''
458
+ q = '''
459
+ [ ou:conference:event=$valu
460
+ :name=$p.name
461
+ :desc=$p.desc
462
+ :start=$p.start
463
+ :end=$p.end
464
+ :conference=$p.conference
465
+ :contact=$p.contact
466
+ :place=$p.place
467
+ :url=$p.url
468
+ ]
469
+
470
+ // :conference should not be RO
471
+ [ -:conference ]
472
+
473
+ // Put the value back
474
+ [ :conference=$p.conference ]
475
+ '''
460
476
  nodes = await core.nodes(q, opts={'vars': {'valu': c0, 'p': props}})
461
477
  self.len(1, nodes)
462
478
  node = nodes[0]
@@ -151,6 +151,10 @@ class PsModelTest(s_t_utils.SynTest):
151
151
  'users': ('visi', 'invisigoth'),
152
152
  'crypto:address': 'btc/1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2',
153
153
  'langs': (lang00 := s_common.guid(),),
154
+ 'banner': file0,
155
+ 'passwd': 'hunter2',
156
+ 'website': 'https://blogs.vertex.link/brutus',
157
+ 'websites': ('https://foo.com', 'https://bar.com', 'https://foo.com'),
154
158
  }
155
159
  opts = {'vars': {'valu': con0, 'p': props}}
156
160
  q = '''[(ps:contact=$valu
@@ -172,6 +176,10 @@ class PsModelTest(s_t_utils.SynTest):
172
176
  :death:place=$p."death:place" :death:place:loc=$p."death:place:loc"
173
177
  :death:place:name=$p."death:place:name"
174
178
  :service:accounts=(*, *) :langs=$p.langs
179
+ :banner=$p.banner
180
+ :passwd=$p.passwd
181
+ :website=$p.website
182
+ :websites=$p.websites
175
183
  )]'''
176
184
  nodes = await core.nodes(q, opts=opts)
177
185
  self.len(1, nodes)
@@ -215,6 +223,10 @@ class PsModelTest(s_t_utils.SynTest):
215
223
  self.eq(node.get('death:place:loc'), 'us.va.reston')
216
224
  self.eq(node.get('birth:place:name'), 'reston, va, usa, earth, sol, milkyway')
217
225
  self.eq(node.get('death:place:name'), 'reston, va, usa, earth, sol, milkyway')
226
+ self.eq(node.get('banner'), file0)
227
+ self.eq(node.get('passwd'), 'hunter2')
228
+ self.eq(node.get('website'), 'https://blogs.vertex.link/brutus')
229
+ self.eq(node.get('websites'), ('https://bar.com', 'https://foo.com'))
218
230
  self.len(1, await core.nodes('ps:contact :birth:place -> geo:place'))
219
231
  self.len(1, await core.nodes('ps:contact :death:place -> geo:place'))
220
232
  self.len(2, await core.nodes('ps:contact :service:accounts -> inet:service:account'))