mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
fix(composio): show connection ID in picker when no identity cached (#3405)
This commit is contained in:
@@ -79,7 +79,7 @@ async function openListbox() {
|
||||
|
||||
describe('deduplicateConnections', () => {
|
||||
it('returns an empty array for empty input', () => {
|
||||
expect(deduplicateConnections([], 'Account')).toEqual([]);
|
||||
expect(deduplicateConnections([])).toEqual([]);
|
||||
});
|
||||
|
||||
it('uses accountEmail as the identity label', () => {
|
||||
@@ -89,7 +89,7 @@ describe('deduplicateConnections', () => {
|
||||
status: 'ACTIVE',
|
||||
accountEmail: 'user@example.com',
|
||||
};
|
||||
const result = deduplicateConnections([conn], 'Account');
|
||||
const result = deduplicateConnections([conn]);
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].label).toBe('Gmail · user@example.com');
|
||||
expect(result[0].conn.id).toBe('conn-1');
|
||||
@@ -97,31 +97,31 @@ describe('deduplicateConnections', () => {
|
||||
|
||||
it('falls back to workspace when accountEmail is absent', () => {
|
||||
const conn = { id: 'conn-2', toolkit: 'Slack', status: 'ACTIVE', workspace: 'my-workspace' };
|
||||
const result = deduplicateConnections([conn], 'Account');
|
||||
const result = deduplicateConnections([conn]);
|
||||
expect(result[0].label).toBe('Slack · my-workspace');
|
||||
});
|
||||
|
||||
it('falls back to username when neither email nor workspace is present', () => {
|
||||
const conn = { id: 'conn-3', toolkit: 'GitHub', status: 'ACTIVE', username: 'octocat' };
|
||||
const result = deduplicateConnections([conn], 'Account');
|
||||
const result = deduplicateConnections([conn]);
|
||||
expect(result[0].label).toBe('GitHub · octocat');
|
||||
});
|
||||
|
||||
it('uses numbered Account label when no identity field is available', () => {
|
||||
it('uses connection ID as label when no identity field is available', () => {
|
||||
const conn = { id: 'conn-x', toolkit: 'Notion', status: 'ACTIVE' };
|
||||
const result = deduplicateConnections([conn], 'Account');
|
||||
expect(result[0].label).toBe('Notion · Account 1');
|
||||
const result = deduplicateConnections([conn]);
|
||||
expect(result[0].label).toBe('Notion · conn-x');
|
||||
});
|
||||
|
||||
it('numbers multiple no-identity connections per toolkit', () => {
|
||||
it('shows each connection ID for multiple no-identity connections', () => {
|
||||
const conns = [
|
||||
{ id: 'conn-a', toolkit: 'Notion', status: 'ACTIVE' },
|
||||
{ id: 'conn-b', toolkit: 'Notion', status: 'ACTIVE' },
|
||||
];
|
||||
const result = deduplicateConnections(conns, 'Account');
|
||||
const result = deduplicateConnections(conns);
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result[0].label).toBe('Notion · Account 1');
|
||||
expect(result[1].label).toBe('Notion · Account 2');
|
||||
expect(result[0].label).toBe('Notion · conn-a');
|
||||
expect(result[1].label).toBe('Notion · conn-b');
|
||||
});
|
||||
|
||||
it('deduplicates connections with the same toolkit and identity', () => {
|
||||
@@ -129,7 +129,7 @@ describe('deduplicateConnections', () => {
|
||||
{ id: 'conn-1', toolkit: 'Gmail', status: 'ACTIVE', accountEmail: 'a@example.com' },
|
||||
{ id: 'conn-2', toolkit: 'Gmail', status: 'ACTIVE', accountEmail: 'a@example.com' },
|
||||
];
|
||||
const result = deduplicateConnections(conns, 'Account');
|
||||
const result = deduplicateConnections(conns);
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].conn.id).toBe('conn-1');
|
||||
expect(result[0].label).toBe('Gmail · a@example.com');
|
||||
@@ -140,32 +140,31 @@ describe('deduplicateConnections', () => {
|
||||
{ id: 'conn-1', toolkit: 'Gmail', status: 'ACTIVE', accountEmail: 'a@example.com' },
|
||||
{ id: 'conn-2', toolkit: 'Gmail', status: 'ACTIVE', accountEmail: 'b@example.com' },
|
||||
];
|
||||
const result = deduplicateConnections(conns, 'Account');
|
||||
const result = deduplicateConnections(conns);
|
||||
expect(result).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('does not expose raw connection IDs in any label', () => {
|
||||
it('uses the connection ID in the label when no identity is available', () => {
|
||||
const conns = [
|
||||
{ id: 'raw-uuid-abc123', toolkit: 'Linear', status: 'ACTIVE' },
|
||||
{ id: 'raw-uuid-def456', toolkit: 'Linear', status: 'ACTIVE' },
|
||||
];
|
||||
const result = deduplicateConnections(conns, 'Account');
|
||||
for (const { label } of result) {
|
||||
expect(label).not.toContain('raw-uuid');
|
||||
}
|
||||
const result = deduplicateConnections(conns);
|
||||
expect(result[0].label).toBe('Linear · raw-uuid-abc123');
|
||||
expect(result[1].label).toBe('Linear · raw-uuid-def456');
|
||||
});
|
||||
|
||||
it('numbers no-identity connections per toolkit independently', () => {
|
||||
it('shows connection IDs for no-identity connections across toolkits', () => {
|
||||
const conns = [
|
||||
{ id: 'n-1', toolkit: 'Notion', status: 'ACTIVE' },
|
||||
{ id: 's-1', toolkit: 'Slack', status: 'ACTIVE' },
|
||||
{ id: 'n-2', toolkit: 'Notion', status: 'ACTIVE' },
|
||||
];
|
||||
const result = deduplicateConnections(conns, 'Account');
|
||||
const result = deduplicateConnections(conns);
|
||||
expect(result).toHaveLength(3);
|
||||
expect(result.find(r => r.conn.id === 'n-1')?.label).toBe('Notion · Account 1');
|
||||
expect(result.find(r => r.conn.id === 'n-2')?.label).toBe('Notion · Account 2');
|
||||
expect(result.find(r => r.conn.id === 's-1')?.label).toBe('Slack · Account 1');
|
||||
expect(result.find(r => r.conn.id === 'n-1')?.label).toBe('Notion · n-1');
|
||||
expect(result.find(r => r.conn.id === 'n-2')?.label).toBe('Notion · n-2');
|
||||
expect(result.find(r => r.conn.id === 's-1')?.label).toBe('Slack · s-1');
|
||||
});
|
||||
|
||||
it('prefers ACTIVE over EXPIRED when deduplicating same toolkit+identity', () => {
|
||||
@@ -174,7 +173,7 @@ describe('deduplicateConnections', () => {
|
||||
{ id: 'conn-expired', toolkit: 'Gmail', status: 'EXPIRED', accountEmail: 'x@example.com' },
|
||||
{ id: 'conn-active', toolkit: 'Gmail', status: 'ACTIVE', accountEmail: 'x@example.com' },
|
||||
];
|
||||
const result = deduplicateConnections(conns, 'Account');
|
||||
const result = deduplicateConnections(conns);
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].conn.id).toBe('conn-active');
|
||||
});
|
||||
@@ -185,7 +184,7 @@ describe('deduplicateConnections', () => {
|
||||
{ id: 'conn-same', toolkit: 'Notion', status: 'ACTIVE' },
|
||||
{ id: 'conn-same', toolkit: 'Notion', status: 'ACTIVE' },
|
||||
];
|
||||
const result = deduplicateConnections(conns, 'Account');
|
||||
const result = deduplicateConnections(conns);
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].conn.id).toBe('conn-same');
|
||||
});
|
||||
@@ -196,7 +195,7 @@ describe('deduplicateConnections', () => {
|
||||
{ id: 'pending', toolkit: 'Linear', status: 'PENDING', accountEmail: 'a@b.com' },
|
||||
{ id: 'connected', toolkit: 'Linear', status: 'CONNECTED', accountEmail: 'a@b.com' },
|
||||
];
|
||||
const result = deduplicateConnections(conns, 'Account');
|
||||
const result = deduplicateConnections(conns);
|
||||
expect(result).toHaveLength(1);
|
||||
// CONNECTED ranks same as ACTIVE — must win over EXPIRED and PENDING
|
||||
expect(result[0].conn.id).toBe('connected');
|
||||
@@ -260,7 +259,7 @@ describe('AddMemorySourceDialog — Composio picker', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('shows numbered Account labels for connections without identity fields', async () => {
|
||||
it('shows connection IDs for connections without identity fields', async () => {
|
||||
mockListConnections.mockResolvedValue({
|
||||
connections: [
|
||||
{ id: 'conn-a', toolkit: 'Notion', status: 'ACTIVE' },
|
||||
@@ -270,12 +269,9 @@ describe('AddMemorySourceDialog — Composio picker', () => {
|
||||
await openComposioStep();
|
||||
await openListbox();
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByText('Notion · Account 1')).toBeTruthy();
|
||||
expect(screen.queryByText('Notion · Account 2')).toBeTruthy();
|
||||
expect(screen.queryByText('Notion · conn-a')).toBeTruthy();
|
||||
expect(screen.queryByText('Notion · conn-b')).toBeTruthy();
|
||||
});
|
||||
// Raw IDs must not appear
|
||||
expect(screen.queryByText('conn-a')).toBeNull();
|
||||
expect(screen.queryByText('conn-b')).toBeNull();
|
||||
});
|
||||
|
||||
it('auto-fills the source label when a connection is selected', async () => {
|
||||
|
||||
@@ -544,16 +544,14 @@ function statusRank(conn: ComposioConnection): number {
|
||||
* - Connections sharing the same toolkit + identity (accountEmail / workspace /
|
||||
* username) OR the same raw connection id are collapsed to the first
|
||||
* occurrence, preventing both labeled and identity-less duplicates.
|
||||
* - Connections with no identity field get a numbered "Account N" suffix
|
||||
* scoped per toolkit, so users can distinguish them without seeing raw IDs.
|
||||
* - Connections with no identity field fall back to showing the raw connection ID
|
||||
* so users can unambiguously distinguish accounts.
|
||||
*/
|
||||
export function deduplicateConnections(
|
||||
connections: ComposioConnection[],
|
||||
accountLabel: string
|
||||
connections: ComposioConnection[]
|
||||
): Array<{ conn: ComposioConnection; label: string }> {
|
||||
const sorted = [...connections].sort((a, b) => statusRank(a) - statusRank(b));
|
||||
const seen = new Set<string>();
|
||||
const unidentifiedCount: Record<string, number> = {};
|
||||
const result: Array<{ conn: ComposioConnection; label: string }> = [];
|
||||
|
||||
for (const conn of sorted) {
|
||||
@@ -574,9 +572,9 @@ export function deduplicateConnections(
|
||||
seen.add(key);
|
||||
result.push({ conn, label: `${conn.toolkit} · ${identity}` });
|
||||
} else {
|
||||
unidentifiedCount[conn.toolkit] = (unidentifiedCount[conn.toolkit] ?? 0) + 1;
|
||||
const n = unidentifiedCount[conn.toolkit];
|
||||
result.push({ conn, label: `${conn.toolkit} · ${accountLabel} ${n}` });
|
||||
// Fall back to the raw connection ID so the user can unambiguously
|
||||
// distinguish accounts when no identity data is available.
|
||||
result.push({ conn, label: `${conn.toolkit} · ${conn.id}` });
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -612,9 +610,8 @@ function ComposioPicker({
|
||||
const listboxRef = useRef<HTMLUListElement>(null);
|
||||
|
||||
// useMemo must be declared before any early returns (Rules of Hooks).
|
||||
const accountLabel = t('memorySources.connectionAccount');
|
||||
const entries = useMemo<PickerEntry[]>(() => {
|
||||
const deduped = deduplicateConnections(connections, accountLabel).map(({ conn, label }) => ({
|
||||
const deduped = deduplicateConnections(connections).map(({ conn, label }) => ({
|
||||
conn,
|
||||
label,
|
||||
supported: isToolkitSupported(conn.toolkit, supportedToolkits),
|
||||
@@ -622,7 +619,7 @@ function ComposioPicker({
|
||||
// Supported connections first so the actionable ones surface at the top;
|
||||
// stable within each partition (dedup already ranked by health/status).
|
||||
return [...deduped.filter(e => e.supported), ...deduped.filter(e => !e.supported)];
|
||||
}, [connections, accountLabel, supportedToolkits]);
|
||||
}, [connections, supportedToolkits]);
|
||||
|
||||
// Indexes of keyboard-selectable (supported) options — unsupported rows are
|
||||
// skipped during arrow navigation, mirroring a native <select>'s disabled opts.
|
||||
|
||||
@@ -1934,7 +1934,6 @@ const messages: TranslationMap = {
|
||||
'لا توجد مصادر عرفية بعد أضف ملفاً، ×1xx repo، ×x0x مكعب، أو صفحة على الشبكة للبدء.',
|
||||
'memorySources.loadingConnections': 'علاقات الحب...',
|
||||
'memorySources.noConnections': 'لم يتم العثور على أي وصلات اكسكساكسية نشطة. إجمع التكامل أولاً',
|
||||
'memorySources.connectionAccount': 'الحساب',
|
||||
'memorySources.pickConnection': 'اختر اتصال',
|
||||
'memorySources.selectConnection': '- اختيار اتصال -',
|
||||
'memorySources.comingSoon': 'قريباً',
|
||||
|
||||
@@ -1973,7 +1973,6 @@ const messages: TranslationMap = {
|
||||
'কোনো স্বনির্ধারিত উৎস পাওয়া যায়নি। একটি ফোল্ডার যোগ করুন, xqx1x rex, xqxqx pxkx ফিড, অথবা ওয়েব পেজ আরম্ভ করুন।',
|
||||
'memorySources.loadingConnections': 'সংযোগ লোড করা হচ্ছে...',
|
||||
'memorySources.noConnections': 'কোনো সক্রিয় xqxqx সংযোগ পাওয়া যায়নি। একটা সম্পর্ক প্রথমে।',
|
||||
'memorySources.connectionAccount': 'অ্যাকাউন্ট',
|
||||
'memorySources.pickConnection': 'একটি সংযোগ নির্বাচন করুন',
|
||||
'memorySources.selectConnection': '- একটি সংযোগ নির্বাচন করুন -',
|
||||
'memorySources.comingSoon': 'শীঘ্রই আসছে',
|
||||
|
||||
@@ -2024,7 +2024,6 @@ const messages: TranslationMap = {
|
||||
'memorySources.loadingConnections': 'Verbindungen werden geladen...',
|
||||
'memorySources.noConnections':
|
||||
'Keine aktiven Composio-Verbindungen gefunden. Verbinden Sie zuerst eine Integration.',
|
||||
'memorySources.connectionAccount': 'Konto',
|
||||
'memorySources.pickConnection': 'Wählen Sie eine Verbindung',
|
||||
'memorySources.selectConnection': 'Wählen Sie eine Verbindung aus.',
|
||||
'memorySources.comingSoon': 'Demnächst',
|
||||
|
||||
@@ -2429,7 +2429,6 @@ const en: TranslationMap = {
|
||||
'memorySources.loadingConnections': 'Loading connections…',
|
||||
'memorySources.noConnections':
|
||||
'No active Composio connections found. Connect an integration first.',
|
||||
'memorySources.connectionAccount': 'Account',
|
||||
'memorySources.pickConnection': 'Pick a connection',
|
||||
'memorySources.selectConnection': '— Select a connection —',
|
||||
'memorySources.comingSoon': 'Coming soon',
|
||||
|
||||
@@ -2016,7 +2016,6 @@ const messages: TranslationMap = {
|
||||
'memorySources.loadingConnections': 'Cargando conexiones…',
|
||||
'memorySources.noConnections':
|
||||
'No se encontraron conexiones activas de Composio. Conecta una integración primero.',
|
||||
'memorySources.connectionAccount': 'Cuenta',
|
||||
'memorySources.pickConnection': 'Elige una conexión',
|
||||
'memorySources.selectConnection': '— Seleccionar una conexión —',
|
||||
'memorySources.comingSoon': 'Próximamente',
|
||||
|
||||
@@ -2022,7 +2022,6 @@ const messages: TranslationMap = {
|
||||
'memorySources.loadingConnections': 'Chargement des connexions…',
|
||||
'memorySources.noConnections':
|
||||
"Aucune connexion Composio active trouvée. Connectez d'abord une intégration.",
|
||||
'memorySources.connectionAccount': 'Compte',
|
||||
'memorySources.pickConnection': 'Choisissez une connexion',
|
||||
'memorySources.selectConnection': '— Sélectionnez une connexion —',
|
||||
'memorySources.comingSoon': 'Bientôt disponible',
|
||||
|
||||
@@ -1974,7 +1974,6 @@ const messages: TranslationMap = {
|
||||
'अभी तक कोई कस्टम स्रोत नहीं है। एक फ़ोल्डर जोड़ें, GitHub रेपो, RSS फीड, या वेब पेज शुरू करने के लिए।',
|
||||
'memorySources.loadingConnections': 'कनेक्शन लोड हो रहा है...',
|
||||
'memorySources.noConnections': 'कोई सक्रिय Composio कनेक्शन नहीं मिला। पहले एकीकरण कनेक्ट करें।',
|
||||
'memorySources.connectionAccount': 'खाता',
|
||||
'memorySources.pickConnection': 'कनेक्शन चुनें',
|
||||
'memorySources.selectConnection': '- एक कनेक्शन चुनें -',
|
||||
'memorySources.comingSoon': 'जल्द आ रहा है',
|
||||
|
||||
@@ -1977,7 +1977,6 @@ const messages: TranslationMap = {
|
||||
'memorySources.loadingConnections': 'Memuat koneksi...',
|
||||
'memorySources.noConnections':
|
||||
'Tak ditemukan koneksi Composio yang aktif. Hubungkan integrasi dulu.',
|
||||
'memorySources.connectionAccount': 'Akun',
|
||||
'memorySources.pickConnection': 'Pilih koneksi',
|
||||
'memorySources.selectConnection': '- Pilih koneksi -',
|
||||
'memorySources.comingSoon': 'Segera hadir',
|
||||
|
||||
@@ -2006,7 +2006,6 @@ const messages: TranslationMap = {
|
||||
'memorySources.loadingConnections': 'Caricamento delle connessioni…',
|
||||
'memorySources.noConnections':
|
||||
"Nessuna connessione Composio attiva trovata. Collega prima un'integrazione.",
|
||||
'memorySources.connectionAccount': 'Account',
|
||||
'memorySources.pickConnection': 'Scegli una connessione',
|
||||
'memorySources.selectConnection': '— Seleziona una connessione —',
|
||||
'memorySources.comingSoon': 'Prossimamente',
|
||||
|
||||
@@ -1951,7 +1951,6 @@ const messages: TranslationMap = {
|
||||
'아직 사용자 지정 소스가 없습니다. 폴더, GitHub 리포지토리, RSS 피드 또는 웹 페이지를 추가해 시작하세요.',
|
||||
'memorySources.loadingConnections': '연결을 불러오는 중…',
|
||||
'memorySources.noConnections': '활성 Composio 연결을 찾을 수 없습니다. 먼저 통합을 연결하세요.',
|
||||
'memorySources.connectionAccount': '계정',
|
||||
'memorySources.pickConnection': '연결 선택',
|
||||
'memorySources.selectConnection': '— 연결 선택 —',
|
||||
'memorySources.comingSoon': '출시 예정',
|
||||
|
||||
@@ -1995,7 +1995,6 @@ const messages: TranslationMap = {
|
||||
'memorySources.loadingConnections': 'Wczytywanie połączeń…',
|
||||
'memorySources.noConnections':
|
||||
'Nie znaleziono aktywnych połączeń Composio. Najpierw połącz integrację.',
|
||||
'memorySources.connectionAccount': 'Konto',
|
||||
'memorySources.pickConnection': 'Wybierz połączenie',
|
||||
'memorySources.selectConnection': '— Wybierz połączenie —',
|
||||
'memorySources.comingSoon': 'Wkrótce',
|
||||
|
||||
@@ -2014,7 +2014,6 @@ const messages: TranslationMap = {
|
||||
'memorySources.loadingConnections': 'Carregando conexões…',
|
||||
'memorySources.noConnections':
|
||||
'Nenhuma conexão Composio ativa encontrada. Conecte uma integração primeiro.',
|
||||
'memorySources.connectionAccount': 'Conta',
|
||||
'memorySources.pickConnection': 'Escolha uma conexão',
|
||||
'memorySources.selectConnection': '— Selecionar uma conexão —',
|
||||
'memorySources.comingSoon': 'Em breve',
|
||||
|
||||
@@ -1987,7 +1987,6 @@ const messages: TranslationMap = {
|
||||
'memorySources.loadingConnections': 'Загрузка подключений…',
|
||||
'memorySources.noConnections':
|
||||
'Активных соединений Composio не найдено. Сначала подключите интеграцию.',
|
||||
'memorySources.connectionAccount': 'Аккаунт',
|
||||
'memorySources.pickConnection': 'Выберите соединение',
|
||||
'memorySources.selectConnection': '— Выберите соединение —',
|
||||
'memorySources.comingSoon': 'Скоро',
|
||||
|
||||
@@ -1873,7 +1873,6 @@ const messages: TranslationMap = {
|
||||
'暂无自定义来源。添加文件夹、GitHub 仓库、RSS 订阅或网页即可开始。',
|
||||
'memorySources.loadingConnections': '正在加载连接…',
|
||||
'memorySources.noConnections': '未找到活跃的 Composio 连接。请先连接一个集成。',
|
||||
'memorySources.connectionAccount': '账户',
|
||||
'memorySources.pickConnection': '选择连接',
|
||||
'memorySources.selectConnection': '— 选择连接 —',
|
||||
'memorySources.comingSoon': '即将推出',
|
||||
|
||||
@@ -1539,7 +1539,15 @@ fn enrich_connections_with_identity(
|
||||
// (normalized_toolkit, normalized_conn_id) → identity
|
||||
let lookup: HashMap<(String, String), _> = identities
|
||||
.iter()
|
||||
.map(|id| ((id.source.clone(), id.identifier.clone()), id))
|
||||
.map(|id| {
|
||||
(
|
||||
(
|
||||
normalize_connection_identifier(&id.source),
|
||||
normalize_connection_identifier(&id.identifier),
|
||||
),
|
||||
id,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
tracing::debug!(
|
||||
|
||||
@@ -2270,14 +2270,12 @@ fn composio_direct_500_does_not_demote() {
|
||||
/// isolated profile storage. The returned guard prevents concurrent tests from
|
||||
/// rebinding the singleton while the calling test is running — hold it with
|
||||
/// `let _guard = init_memory_client(tmp.path());`.
|
||||
fn init_memory_client(workspace: &std::path::Path) -> std::sync::MutexGuard<'static, ()> {
|
||||
static ENRICH_IDENTITY_TEST_LOCK: std::sync::OnceLock<std::sync::Mutex<()>> =
|
||||
std::sync::OnceLock::new();
|
||||
let guard = ENRICH_IDENTITY_TEST_LOCK
|
||||
.get_or_init(|| std::sync::Mutex::new(()))
|
||||
async fn init_memory_client(workspace: &std::path::Path) -> tokio::sync::MutexGuard<'static, ()> {
|
||||
let guard = crate::openhuman::memory::ops::GLOBAL_MEMORY_TEST_LOCK
|
||||
.lock()
|
||||
.unwrap_or_else(|e| e.into_inner());
|
||||
let _ = crate::openhuman::memory::global::init(workspace.to_path_buf());
|
||||
.await;
|
||||
crate::openhuman::memory::global::init(workspace.to_path_buf())
|
||||
.expect("global memory client should initialize for enrichment test");
|
||||
guard
|
||||
}
|
||||
|
||||
@@ -2298,7 +2296,7 @@ async fn enrich_does_nothing_when_no_cached_identities() {
|
||||
// profiles, so load_connected_identities returns Vec::new() and the
|
||||
// connection is returned unchanged.
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let _guard = init_memory_client(tmp.path());
|
||||
let _guard = init_memory_client(tmp.path()).await;
|
||||
let resp = make_connections_response(&[("c1", "gmail", "ACTIVE")]);
|
||||
let enriched = enrich_connections_with_identity(resp);
|
||||
assert_eq!(enriched.connections.len(), 1);
|
||||
@@ -2313,7 +2311,7 @@ async fn enrich_populates_email_from_cached_profile() {
|
||||
profile::persist_provider_profile, ProviderUserProfile,
|
||||
};
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let _guard = init_memory_client(tmp.path());
|
||||
let _guard = init_memory_client(tmp.path()).await;
|
||||
|
||||
persist_provider_profile(&ProviderUserProfile {
|
||||
toolkit: "gmail".to_string(),
|
||||
@@ -2348,7 +2346,7 @@ async fn enrich_populates_handle_for_github() {
|
||||
profile::persist_provider_profile, ProviderUserProfile,
|
||||
};
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let _guard = init_memory_client(tmp.path());
|
||||
let _guard = init_memory_client(tmp.path()).await;
|
||||
|
||||
persist_provider_profile(&ProviderUserProfile {
|
||||
toolkit: "github".to_string(),
|
||||
@@ -2391,7 +2389,7 @@ async fn enrich_handles_multiple_connections_same_toolkit() {
|
||||
profile::persist_provider_profile, ProviderUserProfile,
|
||||
};
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let _guard = init_memory_client(tmp.path());
|
||||
let _guard = init_memory_client(tmp.path()).await;
|
||||
|
||||
persist_provider_profile(&ProviderUserProfile {
|
||||
toolkit: "gmail".to_string(),
|
||||
@@ -2427,12 +2425,12 @@ async fn enrich_handles_multiple_connections_same_toolkit() {
|
||||
#[tokio::test]
|
||||
async fn enrich_leaves_unmatched_connection_unchanged() {
|
||||
// Connection whose id has no cached profile row is returned with all
|
||||
// identity fields as None — the UI falls back to "Account N".
|
||||
// identity fields as None — the UI falls back to "toolkit · connection_id".
|
||||
use crate::openhuman::memory_sync::composio::providers::{
|
||||
profile::persist_provider_profile, ProviderUserProfile,
|
||||
};
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let _guard = init_memory_client(tmp.path());
|
||||
let _guard = init_memory_client(tmp.path()).await;
|
||||
|
||||
// Persist a profile for a DIFFERENT connection id.
|
||||
persist_provider_profile(&ProviderUserProfile {
|
||||
|
||||
Reference in New Issue
Block a user