teams-cli 0.2.3__tar.gz → 0.2.4__tar.gz

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.
Files changed (75) hide show
  1. {teams_cli-0.2.3 → teams_cli-0.2.4}/CHANGELOG.md +11 -0
  2. {teams_cli-0.2.3 → teams_cli-0.2.4}/Cargo.lock +1 -1
  3. {teams_cli-0.2.3 → teams_cli-0.2.4}/Cargo.toml +1 -1
  4. {teams_cli-0.2.3 → teams_cli-0.2.4}/PKG-INFO +1 -1
  5. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/desktop/browser.rs +4 -2
  6. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/desktop/helper.rs +1 -1
  7. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/desktop.rs +37 -6
  8. teams_cli-0.2.4/src/desktop_cache.js +41 -0
  9. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/desktop_people.js +1 -1
  10. {teams_cli-0.2.3 → teams_cli-0.2.4}/tests/desktop_scripts.test.cjs +42 -2
  11. teams_cli-0.2.3/src/desktop_cache.js +0 -17
  12. {teams_cli-0.2.3 → teams_cli-0.2.4}/.github/workflows/ci.yml +0 -0
  13. {teams_cli-0.2.3 → teams_cli-0.2.4}/.github/workflows/dependencies.yml +0 -0
  14. {teams_cli-0.2.3 → teams_cli-0.2.4}/.github/workflows/release.yml +0 -0
  15. {teams_cli-0.2.3 → teams_cli-0.2.4}/.gitignore +0 -0
  16. {teams_cli-0.2.3 → teams_cli-0.2.4}/.mise.toml +0 -0
  17. {teams_cli-0.2.3 → teams_cli-0.2.4}/LICENSE +0 -0
  18. {teams_cli-0.2.3 → teams_cli-0.2.4}/Makefile +0 -0
  19. {teams_cli-0.2.3 → teams_cli-0.2.4}/README.md +0 -0
  20. {teams_cli-0.2.3 → teams_cli-0.2.4}/build.rs +0 -0
  21. {teams_cli-0.2.3 → teams_cli-0.2.4}/docs/releases.md +0 -0
  22. {teams_cli-0.2.3 → teams_cli-0.2.4}/examples/validate_schema.rs +0 -0
  23. {teams_cli-0.2.3 → teams_cli-0.2.4}/pyproject.toml +0 -0
  24. {teams_cli-0.2.3 → teams_cli-0.2.4}/scripts/smoke-release.py +0 -0
  25. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/auth/pass.rs +0 -0
  26. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/auth/process.rs +0 -0
  27. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/auth/wsl.rs +0 -0
  28. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/auth/wsl_credentials.ps1 +0 -0
  29. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/auth.rs +0 -0
  30. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/backend.rs +0 -0
  31. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/cli.rs +0 -0
  32. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/config.rs +0 -0
  33. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/delivery.rs +0 -0
  34. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/desktop/avatars.rs +0 -0
  35. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/desktop/browser_children.rs +0 -0
  36. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/desktop/browser_core.rs +0 -0
  37. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/desktop/browser_install.rs +0 -0
  38. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/desktop/browser_lifetime.rs +0 -0
  39. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/desktop/browser_setup.rs +0 -0
  40. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/desktop/cdp.rs +0 -0
  41. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/desktop/connection.ps1 +0 -0
  42. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/desktop/connection.rs +0 -0
  43. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/desktop/install.rs +0 -0
  44. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/desktop/macos.rs +0 -0
  45. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/desktop/owner.rs +0 -0
  46. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/desktop/session.rs +0 -0
  47. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/desktop/windows.rs +0 -0
  48. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/desktop.js +0 -0
  49. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/desktop_core.js +0 -0
  50. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/desktop_send.js +0 -0
  51. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/error.rs +0 -0
  52. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/graph.rs +0 -0
  53. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/lib.rs +0 -0
  54. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/main.rs +0 -0
  55. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/output.rs +0 -0
  56. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/render.rs +0 -0
  57. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/schema.rs +0 -0
  58. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/setup/capabilities.rs +0 -0
  59. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/setup.rs +0 -0
  60. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/teams_cli_alias.rs +0 -0
  61. {teams_cli-0.2.3 → teams_cli-0.2.4}/src/tui.rs +0 -0
  62. {teams_cli-0.2.3 → teams_cli-0.2.4}/tests/browser_lifetime.rs +0 -0
  63. {teams_cli-0.2.3 → teams_cli-0.2.4}/tests/cli.rs +0 -0
  64. {teams_cli-0.2.3 → teams_cli-0.2.4}/tests/connection.rs +0 -0
  65. {teams_cli-0.2.3 → teams_cli-0.2.4}/tests/connection.test.ps1 +0 -0
  66. {teams_cli-0.2.3 → teams_cli-0.2.4}/tests/credentials.rs +0 -0
  67. {teams_cli-0.2.3 → teams_cli-0.2.4}/tests/desktop.rs +0 -0
  68. {teams_cli-0.2.3 → teams_cli-0.2.4}/tests/desktop_scripts.rs +0 -0
  69. {teams_cli-0.2.3 → teams_cli-0.2.4}/tests/fixtures/clispec-v0.3.json +0 -0
  70. {teams_cli-0.2.3 → teams_cli-0.2.4}/tests/js/people.test.mjs +0 -0
  71. {teams_cli-0.2.3 → teams_cli-0.2.4}/tests/package_alias.rs +0 -0
  72. {teams_cli-0.2.3 → teams_cli-0.2.4}/tests/pass_credentials.sh +0 -0
  73. {teams_cli-0.2.3 → teams_cli-0.2.4}/tests/release_smoke.rs +0 -0
  74. {teams_cli-0.2.3 → teams_cli-0.2.4}/tests/wsl_credentials.test.ps1 +0 -0
  75. {teams_cli-0.2.3 → teams_cli-0.2.4}/vership.toml +0 -0
@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.4](https://github.com/rvben/teams-cli/compare/v0.2.3...v0.2.4) - 2026-10-20
11
+
12
+ ### Fixed
13
+
14
+ - When Teams keeps caches for several accounts, guest organizations or display languages, the desktop backend now reads the one for the signed-in user, tenant and display language instead of refusing.
15
+
16
+ ### Changed
17
+
18
+ - Desktop errors now tell apart two cases: personal and work/school Teams both being open, which `--desktop-account` resolves, and one surface holding cached data for several accounts or organizations, which it does not.
19
+ - Windows desktop helpers use protocol 5. WSL requires the helper from the same CLI release.
20
+
10
21
  ## [0.2.3](https://github.com/rvben/teams-cli/compare/v0.2.2...v0.2.3) - 2026-10-16
11
22
 
12
23
  ### Fixed
@@ -3187,7 +3187,7 @@ dependencies = [
3187
3187
 
3188
3188
  [[package]]
3189
3189
  name = "teams-cli"
3190
- version = "0.2.3"
3190
+ version = "0.2.4"
3191
3191
  dependencies = [
3192
3192
  "assert_cmd",
3193
3193
  "base64 0.23.1",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "teams-cli"
3
- version = "0.2.3"
3
+ version = "0.2.4"
4
4
  edition = "2024"
5
5
  rust-version = "1.90"
6
6
  description = "A Microsoft Teams CLI and TUI designed equally for humans and agents"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: teams-cli
3
- Version: 0.2.3
3
+ Version: 0.2.4
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  License-File: LICENSE
@@ -1690,9 +1690,11 @@ mod tests {
1690
1690
  .unwrap();
1691
1691
  std::fs::set_permissions(&browser, std::fs::Permissions::from_mode(0o700)).unwrap();
1692
1692
  let started = std::time::Instant::now();
1693
- let error = browser_version_with_timeout(&browser, Duration::from_millis(100)).unwrap_err();
1693
+ // Fresh executable inspection on macOS can exceed 100 ms before the
1694
+ // script writes its PID. Allow startup while still bounding the hang.
1695
+ let error = browser_version_with_timeout(&browser, Duration::from_secs(1)).unwrap_err();
1694
1696
  assert!(error.to_string().contains("timed out"));
1695
- assert!(started.elapsed() < Duration::from_secs(2));
1697
+ assert!(started.elapsed() < Duration::from_secs(3));
1696
1698
  let pid: i32 = std::fs::read_to_string(dir.path().join("browser.pid"))
1697
1699
  .unwrap()
1698
1700
  .parse()
@@ -10,7 +10,7 @@ use crate::error::AppError;
10
10
  use serde_json::{Value, json};
11
11
  use tokio::io::{AsyncBufRead, AsyncBufReadExt, AsyncWrite, AsyncWriteExt};
12
12
 
13
- pub const PROTOCOL: u32 = 4;
13
+ pub const PROTOCOL: u32 = 5;
14
14
  const VERSION: &str = env!("CARGO_PKG_VERSION");
15
15
  /// The commit this binary was built from (see `build.rs`), reported in `hello` for
16
16
  /// diagnosis and never compared; empty when unknown.
@@ -71,7 +71,11 @@ pub fn messaging_unsupported() -> AppError {
71
71
  }
72
72
 
73
73
  fn account_selection_error() -> AppError {
74
- AppError::Desktop("Multiple Teams accounts are available; use `init --backend desktop --desktop-account personal` or `--desktop-account work-school` to select a surface. Multiple cached accounts within that surface are still unsupported".into())
74
+ AppError::Desktop("Both personal and work/school Teams are open; use `init --backend desktop --desktop-account personal` or `--desktop-account work-school` to select one".into())
75
+ }
76
+
77
+ fn cached_accounts_error() -> AppError {
78
+ AppError::Desktop("Teams holds cached data for more than one account, organization or display language, and Teams does not record which one is signed in. For a work/school account, Microsoft Graph works instead: `teams init --backend graph`".into())
75
79
  }
76
80
 
77
81
  #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
@@ -676,6 +680,7 @@ fn people_result(reply: &Value, create: bool) -> Result<Value, AppError> {
676
680
  if let Some(code) = value["error"].as_str() {
677
681
  return Err(match code {
678
682
  "account_ambiguous" => account_selection_error(),
683
+ "cache_ambiguous" => cached_accounts_error(),
679
684
  "permission_denied" => AppError::Permission("Teams policy does not allow a chat with this recipient".into()),
680
685
  "search_failed" => AppError::Desktop("Teams could not resolve this person. Check sign-in and try their full email address".into()),
681
686
  "layout" => AppError::Unsupported("This Teams version does not expose a supported recipient interface".into()),
@@ -784,6 +789,7 @@ fn send_error(code: &str) -> AppError {
784
789
  match code {
785
790
  "not_found" => AppError::NotFound("chat not found in the Teams desktop cache".into()),
786
791
  "account_ambiguous" => account_selection_error(),
792
+ "cache_ambiguous" => cached_accounts_error(),
787
793
  "unconfirmed" => AppError::Unconfirmed(
788
794
  "Teams did not confirm the message; it may still be delivered. Check the chat before sending again".into(),
789
795
  ),
@@ -894,6 +900,9 @@ fn read_page(reply: &Value) -> Result<Page, AppError> {
894
900
  if code == "account_ambiguous" {
895
901
  return Err(account_selection_error());
896
902
  }
903
+ if code == "cache_ambiguous" {
904
+ return Err(cached_accounts_error());
905
+ }
897
906
  return Err(AppError::Desktop(match (status, code) {
898
907
  (Some(status), _) => status.detail().into(),
899
908
  (None, "no_target") => {
@@ -928,6 +937,9 @@ fn read_page(reply: &Value) -> Result<Page, AppError> {
928
937
  if value["error"] == "account_ambiguous" {
929
938
  return Err(account_selection_error());
930
939
  }
940
+ if value["error"] == "cache_ambiguous" {
941
+ return Err(cached_accounts_error());
942
+ }
931
943
  let items = value["items"].as_array().ok_or_else(invalid)?.clone();
932
944
  let next_cursor = match &value["next"] {
933
945
  Value::Null => None,
@@ -1219,7 +1231,7 @@ mod tests {
1219
1231
  /// the new one; otherwise just update the expectations.
1220
1232
  #[test]
1221
1233
  fn the_protocol_is_pinned() {
1222
- assert_eq!(helper::PROTOCOL, 4);
1234
+ assert_eq!(helper::PROTOCOL, 5);
1223
1235
  let cursor = encode_cursor(&json!({"t": 1, "id": "m1"})).unwrap();
1224
1236
  assert_eq!(
1225
1237
  read_request(9222, Some(NOTES), 20, Some(&cursor)).unwrap(),
@@ -1258,14 +1270,14 @@ mod tests {
1258
1270
  .map(|b| format!("{b:02x}"))
1259
1271
  .collect::<String>()
1260
1272
  });
1261
- // Protocol 4 adds recipient operations; existing sends retain their request shapes.
1273
+ // Protocol 5 selects the signed-in cache and reports remaining ambiguity apart from surface selection.
1262
1274
  assert_eq!(
1263
1275
  scripts,
1264
1276
  [
1265
1277
  "32ca0293d2d5",
1266
1278
  "baa0bc29c44b",
1267
- "3e837cc21336",
1268
- "3d628a24b464"
1279
+ "025a8edc46b8",
1280
+ "23b1a0e4b293"
1269
1281
  ]
1270
1282
  );
1271
1283
  }
@@ -1300,7 +1312,11 @@ mod tests {
1300
1312
  ));
1301
1313
  assert!(matches!(
1302
1314
  people_result(&page_reply(json!({"error":"account_ambiguous"})), false),
1303
- Err(AppError::Desktop(_))
1315
+ Err(AppError::Desktop(m)) if m.contains("--desktop-account")
1316
+ ));
1317
+ assert!(matches!(
1318
+ people_result(&page_reply(json!({"error":"cache_ambiguous"})), false),
1319
+ Err(AppError::Desktop(m)) if !m.contains("--desktop-account")
1304
1320
  ));
1305
1321
  for id in [
1306
1322
  "someone@example.invalid",
@@ -1469,6 +1485,7 @@ mod tests {
1469
1485
  Err(AppError::NotFound(_))
1470
1486
  ));
1471
1487
  let layout = json!({"result":{"exceptionDetails":{"exception":{"description":"Error: layout\n at x"}}}});
1488
+ // Mixed personal and work/school windows are resolved by choosing a surface.
1472
1489
  for ambiguous in [
1473
1490
  json!({"error": "account_ambiguous"}),
1474
1491
  reply(json!({"error": "account_ambiguous"})),
@@ -1480,6 +1497,20 @@ mod tests {
1480
1497
  matches!(sent_message(&ambiguous, CHAT), Err(AppError::Desktop(m)) if m.contains("--desktop-account"))
1481
1498
  );
1482
1499
  }
1500
+ // Several caches within one surface are not, so no flag is suggested.
1501
+ for ambiguous in [
1502
+ json!({"error": "cache_ambiguous"}),
1503
+ reply(json!({"error": "cache_ambiguous"})),
1504
+ ] {
1505
+ for result in [
1506
+ read_page(&ambiguous).map(|_| ()),
1507
+ sent_message(&ambiguous, CHAT).map(|_| ()),
1508
+ ] {
1509
+ assert!(
1510
+ matches!(result, Err(AppError::Desktop(m)) if m.contains("cached") && !m.contains("--desktop-account"))
1511
+ );
1512
+ }
1513
+ }
1483
1514
  assert!(matches!(read_page(&layout), Err(AppError::Desktop(m)) if m.contains("layout")));
1484
1515
  for reply in [
1485
1516
  json!({"error": "no_target"}),
@@ -0,0 +1,41 @@
1
+ // Select one account's cache databases. Several account caches can coexist in a
2
+ // storage context; their records must never be joined or sent through arbitrarily.
3
+ all => {
4
+ const candidates = kind => all.filter(db =>
5
+ typeof db.name === 'string' && db.name.includes(':' + kind + ':'));
6
+ // Teams names caches `<prefix>:<kind>:<client>:<tenant>:<user>:<locale>`. Other
7
+ // accounts, guest organizations and earlier display languages leave their own; the
8
+ // signed-in one is what Teams itself records, and anything less agreed is refused.
9
+ const signedIn = () => {
10
+ try {
11
+ const item = key => JSON.parse(localStorage.getItem(key) ?? 'null');
12
+ const user = item('tmp.auth.v1.GLOBAL.PrimaryUserId.PrimaryUserId')?.item;
13
+ const primary = item('tmp.react-web-client.cachedPrimaryUser');
14
+ if (typeof user !== 'string' || primary?.userId !== user || typeof primary.tenantId !== 'string') return null;
15
+ const locale = localStorage.getItem(`tmp.${primary.tenantId}.${user}.react-web-client.applicationLocaleCode`);
16
+ return { tenant: primary.tenantId, user, locale };
17
+ } catch {
18
+ return null;
19
+ }
20
+ };
21
+ let conversations = candidates('conversation-manager');
22
+ if (conversations.length > 1) {
23
+ const active = signedIn();
24
+ const part = (db, index) => db.name.split(':')[index];
25
+ if (active) conversations = conversations.filter(db =>
26
+ part(db, 3) === active.tenant && part(db, 4) === active.user);
27
+ if (active && conversations.length > 1) conversations = conversations.filter(db => part(db, 5) === active.locale);
28
+ if (conversations.length !== 1) return { error: 'cache_ambiguous' };
29
+ }
30
+ if (conversations.length !== 1) return { error: 'layout' };
31
+ const conversation = conversations[0].name;
32
+ const account = conversation.split(':')[4];
33
+ if (!account) return { error: 'layout' };
34
+ const related = kind => {
35
+ const sibling = conversation.replace(':conversation-manager:', ':' + kind + ':');
36
+ if (all.some(db => db.name === sibling)) return sibling;
37
+ const matches = candidates(kind).filter(db => db.name.split(':')[4] === account);
38
+ return matches.length === 1 ? matches[0].name : null;
39
+ };
40
+ return { account, conversation, replies: related('replychain-manager'), profiles: related('profiles') };
41
+ }
@@ -281,7 +281,7 @@ async (p, select) => {
281
281
  } finally {
282
282
  db?.close();
283
283
  }
284
- if (own.length !== 1) return { error: own.length ? "account_ambiguous" : "layout" };
284
+ if (own.length !== 1) return { error: own.length ? "cache_ambiguous" : "layout" };
285
285
  if (own[0] === p.recipient) return {id:"48:notes",chatType:"oneOnOne",members:[{userId:own[0]}],source:"desktop"};
286
286
  const userIds = [...new Set([own[0], p.recipient])];
287
287
  const lookup = operationDoc(
@@ -231,10 +231,50 @@ test('a missing account database never falls back to another account', async ()
231
231
  assert.ok(f.opened.every(database => database.endsWith(':account-a')));
232
232
  });
233
233
 
234
+ function selectWith(names, storage = {}) {
235
+ const context = vm.createContext({ localStorage: { getItem: key => key in storage ? storage[key] : null } });
236
+ // Compare plain values: objects from another context have their own prototypes.
237
+ return JSON.parse(JSON.stringify(vm.runInContext(source('desktop_cache.js'), context)(names.map(name => ({ name })))));
238
+ }
239
+
240
+ const teams = (kind, tenant, user, locale = 'en-us') => `Teams:${kind}:react-web-client:${tenant}:${user}:${locale}`;
241
+ const signedIn = (tenant, user, locale) => ({
242
+ 'tmp.auth.v1.GLOBAL.PrimaryUserId.PrimaryUserId': JSON.stringify({ item: user }),
243
+ 'tmp.react-web-client.cachedPrimaryUser': JSON.stringify({ tenantId: tenant, userId: user }),
244
+ ...(locale && { [`tmp.${tenant}.${user}.react-web-client.applicationLocaleCode`]: locale }),
245
+ });
246
+ const caches = (tenant, user, locale) => ['conversation-manager', 'replychain-manager', 'profiles']
247
+ .map(kind => teams(kind, tenant, user, locale));
248
+
249
+ test('the signed-in user, tenant and display language select among coexisting caches', () => {
250
+ const names = [
251
+ ...caches('home', 'me', 'en-us'), ...caches('home', 'me', 'nl-nl'),
252
+ ...caches('guest', 'me-as-guest'), ...caches('home', 'colleague'),
253
+ ];
254
+ for (const [tenant, user, locale] of [['home', 'me', 'nl-nl'], ['home', 'me', 'en-us'], ['guest', 'me-as-guest', 'en-us']]) {
255
+ assert.deepEqual(selectWith(names, signedIn(tenant, user, locale)), {
256
+ account: user, conversation: teams('conversation-manager', tenant, user, locale),
257
+ replies: teams('replychain-manager', tenant, user, locale), profiles: teams('profiles', tenant, user, locale),
258
+ });
259
+ }
260
+ // Only one cache for the signed-in account needs no display language.
261
+ assert.equal(selectWith(names, signedIn('home', 'colleague')).conversation, teams('conversation-manager', 'home', 'colleague'));
262
+ });
263
+
264
+ test('caches are refused when Teams does not identify exactly one of them', () => {
265
+ const names = [...caches('home', 'me', 'en-us'), ...caches('home', 'me', 'nl-nl'), ...caches('home', 'colleague')];
266
+ const inconsistent = { ...signedIn('home', 'me', 'en-us'),
267
+ 'tmp.react-web-client.cachedPrimaryUser': JSON.stringify({ tenantId: 'home', userId: 'colleague' }) };
268
+ for (const storage of [{}, signedIn('home', 'me'), signedIn('home', 'me', 'de-de'), signedIn('home', 'stranger', 'en-us'), inconsistent,
269
+ { 'tmp.auth.v1.GLOBAL.PrimaryUserId.PrimaryUserId': 'not json' }]) {
270
+ assert.deepEqual(selectWith(names, storage), { error: 'cache_ambiguous' });
271
+ }
272
+ });
273
+
234
274
  test('ambiguous account caches are refused before reading or sending', async () => {
235
275
  const f = fixture({ ambiguous: true });
236
- assert.equal((await f.read()).error, 'account_ambiguous');
237
- assert.equal((await f.send()).error, 'account_ambiguous');
276
+ assert.equal((await f.read()).error, 'cache_ambiguous');
277
+ assert.equal((await f.send()).error, 'cache_ambiguous');
238
278
  assert.deepEqual(f.opened, []);
239
279
  assert.deepEqual(f.mutations, []);
240
280
  });
@@ -1,17 +0,0 @@
1
- // Select one account's cache databases. Several account caches can coexist in a
2
- // storage context; their records must never be joined or sent through arbitrarily.
3
- all => {
4
- const candidates = kind => all.filter(db =>
5
- typeof db.name === 'string' && db.name.includes(':' + kind + ':'));
6
- const conversations = candidates('conversation-manager');
7
- if (conversations.length > 1) return { error: 'account_ambiguous' };
8
- if (conversations.length !== 1) return { error: 'layout' };
9
- const conversation = conversations[0].name;
10
- const account = conversation.split(':')[4];
11
- if (!account) return { error: 'layout' };
12
- const related = kind => {
13
- const matches = candidates(kind).filter(db => db.name.split(':')[4] === account);
14
- return matches.length === 1 ? matches[0].name : null;
15
- };
16
- return { account, conversation, replies: related('replychain-manager'), profiles: related('profiles') };
17
- }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes