|
1 | 1 | import { describe, expect, it, vi } from 'vitest'; |
2 | 2 | import { getRegistry } from '@jackwener/opencli/registry'; |
3 | | -import './list-add.js'; |
| 3 | +import { ArgumentError, CommandExecutionError } from '@jackwener/opencli/errors'; |
| 4 | +import { buildListAddMemberRow } from './list-add.js'; |
4 | 5 |
|
5 | 6 | describe('twitter list-add registration', () => { |
6 | 7 | it('registers the list-add command with the expected shape', () => { |
@@ -34,4 +35,99 @@ describe('twitter list-add registration', () => { |
34 | 35 | expect(page.wait).toHaveBeenCalledWith(3); |
35 | 36 | expect(page.getCookies).toHaveBeenCalledWith({ url: 'https://x.com' }); |
36 | 37 | }); |
| 38 | + |
| 39 | + it('rejects invalid user input before navigation', async () => { |
| 40 | + const cmd = getRegistry().get('twitter/list-add'); |
| 41 | + const page = { |
| 42 | + goto: vi.fn(), |
| 43 | + wait: vi.fn(), |
| 44 | + getCookies: vi.fn(), |
| 45 | + evaluate: vi.fn(), |
| 46 | + }; |
| 47 | + |
| 48 | + await expect(cmd.func(page, { listId: 'abc', username: 'alice' })).rejects.toBeInstanceOf(ArgumentError); |
| 49 | + await expect(cmd.func(page, { listId: '123', username: '' })).rejects.toBeInstanceOf(ArgumentError); |
| 50 | + expect(page.goto).not.toHaveBeenCalled(); |
| 51 | + }); |
| 52 | + |
| 53 | + it('builds success rows when member_count increases despite non-fatal decode errors', () => { |
| 54 | + const row = buildListAddMemberRow({ |
| 55 | + addResult: { |
| 56 | + httpOk: true, |
| 57 | + status: 200, |
| 58 | + mc: 11, |
| 59 | + isMember: true, |
| 60 | + errors: [{ path: ['data', 'list', 'default_banner_media_results'], message: 'decode failed' }], |
| 61 | + }, |
| 62 | + memberCountBefore: 10, |
| 63 | + listId: '123', |
| 64 | + username: 'alice', |
| 65 | + userId: '42', |
| 66 | + }); |
| 67 | + |
| 68 | + expect(row).toMatchObject({ |
| 69 | + listId: '123', |
| 70 | + username: 'alice', |
| 71 | + userId: '42', |
| 72 | + status: 'success', |
| 73 | + }); |
| 74 | + expect(row.message).toContain('member_count 10 → 11'); |
| 75 | + }); |
| 76 | + |
| 77 | + it('treats unchanged member_count as noop only when membership is confirmed', () => { |
| 78 | + const row = buildListAddMemberRow({ |
| 79 | + addResult: { httpOk: true, status: 200, mc: 10, isMember: true, errors: null }, |
| 80 | + memberCountBefore: 10, |
| 81 | + listId: '123', |
| 82 | + username: 'alice', |
| 83 | + userId: '42', |
| 84 | + }); |
| 85 | + |
| 86 | + expect(row.status).toBe('noop'); |
| 87 | + expect(row.message).toBe('@alice is already a member of list 123'); |
| 88 | + }); |
| 89 | + |
| 90 | + it('fails typed when unchanged member_count does not confirm membership', () => { |
| 91 | + expect(() => buildListAddMemberRow({ |
| 92 | + addResult: { httpOk: true, status: 200, mc: 10, isMember: false, errors: null }, |
| 93 | + memberCountBefore: 10, |
| 94 | + listId: '123', |
| 95 | + username: 'alice', |
| 96 | + userId: '42', |
| 97 | + })).toThrow(CommandExecutionError); |
| 98 | + }); |
| 99 | + |
| 100 | + it('fails typed when member_count decreases unexpectedly', () => { |
| 101 | + expect(() => buildListAddMemberRow({ |
| 102 | + addResult: { httpOk: true, status: 200, mc: 9, isMember: true, errors: null }, |
| 103 | + memberCountBefore: 10, |
| 104 | + listId: '123', |
| 105 | + username: 'alice', |
| 106 | + userId: '42', |
| 107 | + })).toThrow(/decreased unexpectedly/); |
| 108 | + }); |
| 109 | + |
| 110 | + it('fails typed when GraphQL response has no usable member_count', () => { |
| 111 | + expect(() => buildListAddMemberRow({ |
| 112 | + addResult: { |
| 113 | + httpOk: true, |
| 114 | + status: 200, |
| 115 | + mc: undefined, |
| 116 | + isMember: null, |
| 117 | + errors: [{ message: 'List is unavailable', path: ['data', 'list'] }], |
| 118 | + }, |
| 119 | + memberCountBefore: 10, |
| 120 | + listId: '123', |
| 121 | + username: 'alice', |
| 122 | + userId: '42', |
| 123 | + })).toThrow(/List is unavailable/); |
| 124 | + |
| 125 | + expect(() => buildListAddMemberRow({ |
| 126 | + addResult: { httpOk: true, status: 200, mc: null, isMember: null, errors: { message: 'not an array' } }, |
| 127 | + memberCountBefore: 10, |
| 128 | + listId: '123', |
| 129 | + username: 'alice', |
| 130 | + userId: '42', |
| 131 | + })).toThrow(/no member_count/); |
| 132 | + }); |
37 | 133 | }); |
0 commit comments