Files
cls 9dfa06ffee
Docker image / Build (linux/amd64) (push) Has been cancelled
Docker image / Build (linux/arm64) (push) Has been cancelled
Docker image / Merge release multi-arch manifest (push) Has been cancelled
Docker image / Merge debug multi-arch manifest (push) Has been cancelled
Docker image / Build public push gateway (linux/amd64) (push) Has been cancelled
Docker image / Build public push gateway (linux/arm64) (push) Has been cancelled
Docker image / Publish public push gateway image (push) Has been cancelled
Sprig image / Build (linux/amd64) (push) Has been cancelled
Sprig image / Build (linux/arm64) (push) Has been cancelled
Sprig image / Merge multi-arch manifest (push) Has been cancelled
Harbor Buzz Orchestra / Python tests and lint (push) Has been cancelled
CI / Detect Changed Paths (push) Has been cancelled
CI / Rust Lint (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Desktop Core (push) Has been cancelled
CI / Desktop Smoke E2E (1) (push) Has been cancelled
CI / Desktop Smoke E2E (2) (push) Has been cancelled
CI / Desktop Smoke E2E (3) (push) Has been cancelled
CI / Desktop Smoke E2E (4) (push) Has been cancelled
CI / Desktop (push) Has been cancelled
CI / Desktop E2E Relay (push) Has been cancelled
CI / Desktop E2E Integration (1/2) (push) Has been cancelled
CI / Desktop E2E Integration (2/2) (push) Has been cancelled
CI / Desktop E2E Integration (push) Has been cancelled
CI / Backend Integration (relay e2e) (push) Has been cancelled
CI / Relay E2E (push) Has been cancelled
CI / Web (push) Has been cancelled
CI / Mobile (push) Has been cancelled
CI / Security (push) Has been cancelled
CI / Dead Token Reference Guard (push) Has been cancelled
CI / Server Cross-Compile (aarch64-unknown-linux-musl) (push) Has been cancelled
CI / Server Cross-Compile (x86_64-unknown-linux-musl) (push) Has been cancelled
CI / Windows Rust (x86_64-pc-windows-msvc) (push) Has been cancelled
CI / Desktop Build (macOS) (push) Has been cancelled
helm chart / lint + unittest + render matrix (push) Has been cancelled
helm chart / install on kind (gated) (push) Has been cancelled
helm chart / publish chart to GHCR (push) Has been cancelled
Mesh Lifecycle / Relay-Driven Mesh Lifecycle Smoke (push) Has been cancelled
Sprig / Build (aarch64-unknown-linux-musl) (push) Has been cancelled
Sprig / Build (x86_64-unknown-linux-musl) (push) Has been cancelled
Sprig / Publish rolling release (push) Has been cancelled
Sprig / Publish tagged release (push) Has been cancelled
feat: import Chinese-localized Buzz source snapshot
Signed-off-by: cls_宁波本机 <908705107@qq.com>
2026-08-13 18:34:25 +08:00

166 lines
5.0 KiB
Dart

import 'package:flutter_test/flutter_test.dart';
import 'package:buzz/shared/custom_emoji/custom_emoji.dart';
import 'package:buzz/shared/relay/nostr_models.dart';
NostrEvent _event(
String pubkey,
List<List<String>> emojiTags, {
int createdAt = 0,
}) {
return NostrEvent(
id: 'id-$pubkey',
pubkey: pubkey,
createdAt: createdAt,
kind: kindEmojiSet,
tags: [
['d', customEmojiSetDTag],
...emojiTags,
],
content: '',
sig: '',
);
}
void main() {
group('normalizeShortcode', () {
test('strips colons and lowercases', () {
expect(normalizeShortcode(':PartyParrot:'), 'partyparrot');
expect(normalizeShortcode(' meow '), 'meow');
expect(normalizeShortcode('a_b-c1'), 'a_b-c1');
});
test('rejects invalid chars and empty', () {
expect(normalizeShortcode(':: ::'), isNull);
expect(normalizeShortcode('has space'), isNull);
expect(normalizeShortcode('emoji!'), isNull);
expect(normalizeShortcode(''), isNull);
});
});
group('customEmojiFromTags', () {
test('parses valid emoji tags, normalizing shortcodes', () {
final result = customEmojiFromTags([
['emoji', 'Meow', 'https://a/meow.png'],
['p', 'someone'],
['emoji', ':Wave:', 'https://a/wave.png'],
]);
expect(result, [
const CustomEmoji(shortcode: 'meow', url: 'https://a/meow.png'),
const CustomEmoji(shortcode: 'wave', url: 'https://a/wave.png'),
]);
});
test('skips malformed and dup-within-event (first wins)', () {
final result = customEmojiFromTags([
['emoji', 'meow'], // missing url
['emoji', '', 'https://a/x.png'], // empty shortcode
['emoji', 'meow', 'https://a/meow1.png'],
['emoji', 'meow', 'https://a/meow2.png'], // dup
]);
expect(result, [
const CustomEmoji(shortcode: 'meow', url: 'https://a/meow1.png'),
]);
});
});
group('unionCustomEmoji', () {
test(
'collapses to one per shortcode; same-time tie-breaks to smaller URL',
() {
final palette = unionCustomEmoji([
_event('alice', [
['emoji', 'meow', 'https://z/meow.png'],
['emoji', 'wave', 'https://a/wave.png'],
]),
_event('bob', [
['emoji', 'meow', 'https://a/meow.png'], // tie → smaller URL wins
]),
]);
expect(palette, [
const CustomEmoji(shortcode: 'meow', url: 'https://a/meow.png'),
const CustomEmoji(shortcode: 'wave', url: 'https://a/wave.png'),
]);
},
);
test('most recently published set wins, regardless of URL order', () {
final older = _event('alice', [
['emoji', 'x', 'https://a/x.png'],
], createdAt: 100);
final newer = _event('bob', [
['emoji', 'x', 'https://z/x.png'],
], createdAt: 200);
const expected = [CustomEmoji(shortcode: 'x', url: 'https://z/x.png')];
expect(unionCustomEmoji([older, newer]), expected);
expect(unionCustomEmoji([newer, older]), expected);
});
test('deterministic regardless of event order', () {
final a = _event('alice', [
['emoji', 'x', 'https://z/x.png'],
]);
final b = _event('bob', [
['emoji', 'x', 'https://a/x.png'],
]);
expect(unionCustomEmoji([a, b]), unionCustomEmoji([b, a]));
});
test('empty input → empty palette', () {
expect(unionCustomEmoji([]), isEmpty);
});
});
group('buildCustomEmojiTags', () {
final palette = [
const CustomEmoji(shortcode: 'meow', url: 'https://a/meow.png'),
const CustomEmoji(shortcode: 'wave', url: 'https://a/wave.png'),
];
test(
'emits one tag per distinct known shortcode, first-appearance order',
() {
final tags = buildCustomEmojiTags(
'hi :wave: and :meow: and :wave:',
palette,
);
expect(tags, [
['emoji', 'wave', 'https://a/wave.png'],
['emoji', 'meow', 'https://a/meow.png'],
]);
},
);
test('case-insensitive match, canonical lowercase emitted', () {
final tags = buildCustomEmojiTags(':MEOW:', palette);
expect(tags, [
['emoji', 'meow', 'https://a/meow.png'],
]);
});
test('ignores unknown shortcodes', () {
expect(buildCustomEmojiTags(':unknown:', palette), isEmpty);
});
test('empty palette → no tags', () {
expect(buildCustomEmojiTags(':meow:', const []), isEmpty);
});
});
group('reactionEmojiUrl', () {
final palette = [
const CustomEmoji(shortcode: 'meow', url: 'https://a/meow.png'),
];
test('resolves a known custom-emoji reaction', () {
expect(reactionEmojiUrl(':meow:', palette), 'https://a/meow.png');
expect(reactionEmojiUrl(':MEOW:', palette), 'https://a/meow.png');
});
test('returns null for unicode / unknown / no palette', () {
expect(reactionEmojiUrl('👍', palette), isNull);
expect(reactionEmojiUrl(':unknown:', palette), isNull);
expect(reactionEmojiUrl(':meow:', null), isNull);
});
});
}