Files
zeekay cd654e372e feat(teams): Hanzo AI for Microsoft Teams (@hanzo/teams)
A Bot Framework (botbuilder) app bringing Hanzo AI into Microsoft Teams,
built on the shared foundation — @hanzo/ai (headless client), @hanzo/iam
(identity), @hanzo/cards (canonical PanelSpec -> Adaptive Card).

- Bot (TeamsActivityHandler): 1:1 + channel chat; @mention/DM a prompt ->
  completion -> reply as a Hanzo assistant Adaptive Card panel.
- Adaptive Card actions: model picker (Input.ChoiceSet), quick actions
  (Draft/Summarize/Explain/Extract action items), prompt input + submit,
  all routed by dispatch() on data.action (from @hanzo/cards).
- Message extension "Ask Hanzo" (action) on a message + "Compose with
  Hanzo" (query) to insert an AI draft; pulls message/thread context.
- Teams app manifest (schema v1.16) + color/outline icons + a dependency-
  free zip build step for the sideloadable app package.
- Pure logic modules (dispatch/panels/context/hanzo/identity/config) with
  vitest coverage; tsc clean; tsup build (library + runnable server).

api.hanzo.ai /v1 via @hanzo/ai only; secrets are env-only (never committed).
2026-07-03 19:19:13 -07:00

56 lines
1.6 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import {
parseSub,
identityFromClaims,
anonymous,
tenantIdentity,
} from '../src/identity';
describe('parseSub', () => {
it('splits org/username', () => {
expect(parseSub('acme/alice')).toEqual({ org: 'acme', username: 'alice' });
});
it('treats a value with no slash as a bare username', () => {
expect(parseSub('alice')).toEqual({ username: 'alice' });
});
it('splits on the first slash without leaking the separator', () => {
expect(parseSub('acme/')).toEqual({ org: 'acme' });
expect(parseSub('/alice')).toEqual({ username: 'alice' });
expect(parseSub('acme/team/alice')).toEqual({ org: 'acme', username: 'team/alice' });
});
it('returns empty for undefined', () => {
expect(parseSub(undefined)).toEqual({});
});
});
describe('identityFromClaims', () => {
it('builds a signed-in identity from org/username + email', () => {
expect(identityFromClaims({ sub: 'acme/bob', email: '[email protected]' })).toEqual({
signedIn: true,
org: 'acme',
username: 'bob',
email: '[email protected]',
});
});
it('omits absent fields', () => {
expect(identityFromClaims({ sub: 'solo' })).toEqual({
signedIn: true,
username: 'solo',
});
});
});
describe('tenantIdentity / anonymous', () => {
it('is signed in when a tenant key exists', () => {
expect(tenantIdentity(true)).toEqual({ signedIn: true });
});
it('is signed out with no key', () => {
expect(tenantIdentity(false)).toEqual({ signedIn: false });
expect(anonymous()).toEqual({ signedIn: false });
});
});