mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2024-11-21 09:36:35 +00:00
refactor: jest test
This commit is contained in:
parent
24d3b52e0b
commit
0522ba35fe
@ -19,10 +19,9 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.24.7",
|
"@babel/core": "^7.24.7",
|
||||||
"@babel/preset-typescript": "^7.24.7",
|
|
||||||
"vite-plugin-babel": "^1.2.0",
|
|
||||||
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
||||||
"@babel/plugin-proposal-decorators": "^7.24.7",
|
"@babel/plugin-proposal-decorators": "^7.24.7",
|
||||||
|
"@babel/preset-typescript": "^7.24.7",
|
||||||
"@log4js-node/log4js-api": "^1.0.2",
|
"@log4js-node/log4js-api": "^1.0.2",
|
||||||
"@protobuf-ts/plugin": "^2.9.4",
|
"@protobuf-ts/plugin": "^2.9.4",
|
||||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||||
@ -31,6 +30,7 @@
|
|||||||
"@types/express": "^4.17.21",
|
"@types/express": "^4.17.21",
|
||||||
"@types/figlet": "^1.5.8",
|
"@types/figlet": "^1.5.8",
|
||||||
"@types/fluent-ffmpeg": "^2.1.24",
|
"@types/fluent-ffmpeg": "^2.1.24",
|
||||||
|
"@types/jest": "^29.5.12",
|
||||||
"@types/node": "^20.11.30",
|
"@types/node": "^20.11.30",
|
||||||
"@types/qrcode-terminal": "^0.12.2",
|
"@types/qrcode-terminal": "^0.12.2",
|
||||||
"@types/uuid": "^10.0.0",
|
"@types/uuid": "^10.0.0",
|
||||||
@ -47,6 +47,7 @@
|
|||||||
"rollup-plugin-obfuscator": "^1.1.0",
|
"rollup-plugin-obfuscator": "^1.1.0",
|
||||||
"typescript": "^5.3.3",
|
"typescript": "^5.3.3",
|
||||||
"vite": "^5.2.6",
|
"vite": "^5.2.6",
|
||||||
|
"vite-plugin-babel": "^1.2.0",
|
||||||
"vite-plugin-cp": "^4.0.8",
|
"vite-plugin-cp": "^4.0.8",
|
||||||
"vite-plugin-dts": "^3.8.2",
|
"vite-plugin-dts": "^3.8.2",
|
||||||
"vite-tsconfig-paths": "^4.3.2"
|
"vite-tsconfig-paths": "^4.3.2"
|
||||||
|
@ -1,63 +1,30 @@
|
|||||||
|
import { Peer } from '@/core';
|
||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
|
|
||||||
class CircularQueue<T> {
|
|
||||||
private items: T[] = [];
|
|
||||||
private front: number = 0;
|
|
||||||
private maxSize: number;
|
|
||||||
constructor(maxSize: number) {
|
|
||||||
this.maxSize = maxSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
enqueue(item: T): void {
|
|
||||||
if (this.items.length == this.maxSize) {
|
|
||||||
this.front = (this.front + 1) % this.maxSize;
|
|
||||||
}
|
|
||||||
this.items[(this.front + this.items.length) % this.maxSize] = item;
|
|
||||||
}
|
|
||||||
|
|
||||||
dequeue(): T | undefined {
|
|
||||||
return this.items.length == 0 ? undefined : this.items[(this.front + 1) % this.items.length];
|
|
||||||
}
|
|
||||||
|
|
||||||
size(): number {
|
|
||||||
return this.items.length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class LimitedHashTable<K, V> {
|
class LimitedHashTable<K, V> {
|
||||||
private keyToValue: Map<K, V> = new Map();
|
private keyToValue: Map<K, V> = new Map();
|
||||||
private valueToKey: Map<V, K> = new Map();
|
private valueToKey: Map<V, K> = new Map();
|
||||||
private keyQueue: CircularQueue<K> = new CircularQueue<K>(0);
|
|
||||||
private valueQueue: CircularQueue<V> = new CircularQueue<V>(0);
|
|
||||||
private maxSize: number;
|
private maxSize: number;
|
||||||
|
|
||||||
constructor(maxSize: number) {
|
constructor(maxSize: number) {
|
||||||
this.maxSize = maxSize;
|
this.maxSize = maxSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
private removeFromQueues(key: K, value: V): void {
|
|
||||||
this.keyQueue.dequeue();
|
|
||||||
this.valueQueue.dequeue();
|
|
||||||
}
|
|
||||||
|
|
||||||
set(key: K, value: V): void {
|
set(key: K, value: V): void {
|
||||||
let isExist = this.keyToValue.get(key);
|
const isExist = this.keyToValue.get(key);
|
||||||
if (isExist && isExist === value) {
|
if (isExist && isExist === value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.keyToValue.set(key, value);
|
this.keyToValue.set(key, value);
|
||||||
this.valueToKey.set(value, key);
|
this.valueToKey.set(value, key);
|
||||||
this.keyQueue.enqueue(key);
|
while (this.keyToValue.size !== this.valueToKey.size){
|
||||||
this.valueQueue.enqueue(value);
|
console.log('keyToValue.size !== valueToKey.size');
|
||||||
|
}
|
||||||
if (this.keyQueue.size() > this.maxSize || this.valueQueue.size() > this.maxSize) {
|
while (this.keyToValue.size > this.maxSize || this.valueToKey.size > this.maxSize) {
|
||||||
const removedKey = this.keyQueue.dequeue();
|
//删除旧的值
|
||||||
const removedValue = this.valueQueue.dequeue();
|
const oldestKey = this.keyToValue.keys().next().value;
|
||||||
if (removedKey !== undefined && removedValue !== undefined) {
|
this.keyToValue.delete(oldestKey);
|
||||||
this.keyToValue.delete(removedKey);
|
this.valueToKey.delete(oldestKey);
|
||||||
this.valueToKey.delete(removedValue);
|
|
||||||
this.removeFromQueues(removedKey, removedValue);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,12 +36,11 @@ class LimitedHashTable<K, V> {
|
|||||||
return this.valueToKey.get(value);
|
return this.valueToKey.get(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteByValue(value: V) {
|
deleteByValue(value: V): void {
|
||||||
const key = this.valueToKey.get(value);
|
const key = this.valueToKey.get(value);
|
||||||
if (key !== undefined) {
|
if (key !== undefined) {
|
||||||
this.keyToValue.delete(key);
|
this.keyToValue.delete(key);
|
||||||
this.valueToKey.delete(value);
|
this.valueToKey.delete(value);
|
||||||
this.removeFromQueues(key, value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,41 +49,47 @@ class LimitedHashTable<K, V> {
|
|||||||
if (value !== undefined) {
|
if (value !== undefined) {
|
||||||
this.keyToValue.delete(key);
|
this.keyToValue.delete(key);
|
||||||
this.valueToKey.delete(value);
|
this.valueToKey.delete(value);
|
||||||
this.removeFromQueues(key, value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MessageUniqueWrapper {
|
class MessageUniqueWrapper {
|
||||||
private msgIdMap: LimitedHashTable<string, number>;
|
private msgIdMap: LimitedHashTable<string, number>;
|
||||||
constructor(MaxMap: number = 1000) {
|
constructor(maxMap: number = 1000) {
|
||||||
this.msgIdMap = new LimitedHashTable<string, number>(MaxMap);
|
this.msgIdMap = new LimitedHashTable<string, number>(maxMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
createMsg(MsgId: string) {
|
createMsg(peer: Peer, msgId: string): number | undefined {
|
||||||
const hash = crypto.createHash('sha256');
|
const key = `${msgId}|${peer.chatType}|${peer.peerUid}`;
|
||||||
hash.update(MsgId);
|
const hash = crypto.createHash('sha1').update(key);
|
||||||
const ShortId = parseInt(hash.digest('hex').slice(0, 8), 16);
|
const shortId = parseInt(hash.digest('hex').slice(0, 8), 16);
|
||||||
let isExist = this.msgIdMap.getKey(ShortId)
|
const isExist = this.msgIdMap.getKey(shortId);
|
||||||
if (isExist && isExist === MsgId) {
|
if (isExist && isExist === msgId) {
|
||||||
return true;
|
return undefined;
|
||||||
}
|
}
|
||||||
return this.msgIdMap.set(MsgId, ShortId);
|
|
||||||
|
this.msgIdMap.set(key, shortId);
|
||||||
|
return shortId;
|
||||||
}
|
}
|
||||||
|
|
||||||
getMsgIdByShortId(ShortId: number) {
|
getMsgIdAndPeerByShortId(shortId: number): { MsgId: string; Peer: Peer } | undefined {
|
||||||
return this.msgIdMap.getKey(ShortId);
|
const data = this.msgIdMap.getKey(shortId);
|
||||||
|
if (data) {
|
||||||
|
const [msgId, chatTypeStr, peerUid] = data.split('|');
|
||||||
|
const peer: Peer = {
|
||||||
|
chatType: parseInt(chatTypeStr),
|
||||||
|
peerUid,
|
||||||
|
guildId: '',
|
||||||
|
};
|
||||||
|
return { MsgId: msgId, Peer: peer };
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
getShortIdByMsgId(MsgId: string) {
|
getShortIdByMsgId(msgId: string): number | undefined {
|
||||||
return this.msgIdMap.getValue(MsgId);
|
return this.msgIdMap.getValue(msgId);
|
||||||
}
|
|
||||||
|
|
||||||
CreateMsgIdList(MsgIdList: string[]) {
|
|
||||||
return MsgIdList.map((MsgId) => {
|
|
||||||
return this.createMsg(MsgId);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MessageUnique = new MessageUniqueWrapper();
|
export const MessageUnique = new MessageUniqueWrapper();
|
||||||
|
|
||||||
|
95
test/MessageUnique.ts
Normal file
95
test/MessageUnique.ts
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
describe('MessageUniqueWrapper', () => {
|
||||||
|
let messageUniqueWrapper: MessageUniqueWrapper;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
messageUniqueWrapper = new MessageUniqueWrapper();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('createMsg should return a unique shortId for a new message', () => {
|
||||||
|
const peer = new Peer();
|
||||||
|
peer.chatType = 1;
|
||||||
|
peer.peerUid = '123';
|
||||||
|
const msgId = 'msgId123';
|
||||||
|
|
||||||
|
const key = `${msgId}|${peer.chatType}|${peer.peerUid}`;
|
||||||
|
const hash = crypto.createHash('sha1').update(key);
|
||||||
|
const expectedShortId = parseInt(hash.digest('hex').slice(0, 8), 16);
|
||||||
|
|
||||||
|
const shortId = messageUniqueWrapper.createMsg(peer, msgId);
|
||||||
|
|
||||||
|
expect(shortId).toBeDefined();
|
||||||
|
expect(shortId).toBe(expectedShortId);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('createMsg should return undefined if the same message is added again', () => {
|
||||||
|
const peer = new Peer();
|
||||||
|
peer.chatType = 1;
|
||||||
|
peer.peerUid = '123';
|
||||||
|
const msgId = 'msgId123';
|
||||||
|
|
||||||
|
const shortId = messageUniqueWrapper.createMsg(peer, msgId);
|
||||||
|
const secondShortId = messageUniqueWrapper.createMsg(peer, msgId);
|
||||||
|
|
||||||
|
expect(shortId).toBeDefined();
|
||||||
|
expect(secondShortId).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getMsgIdAndPeerByShortId should return the message and peer for a given shortId', () => {
|
||||||
|
const peer = new Peer();
|
||||||
|
peer.chatType = 1;
|
||||||
|
peer.peerUid = '123';
|
||||||
|
const msgId = 'msgId123';
|
||||||
|
|
||||||
|
messageUniqueWrapper.createMsg(peer, msgId);
|
||||||
|
|
||||||
|
const shortId = messageUniqueWrapper.getShortIdByMsgId(msgId);
|
||||||
|
const result = messageUniqueWrapper.getMsgIdAndPeerByShortId(shortId);
|
||||||
|
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
expect(result.MsgId).toBe(msgId);
|
||||||
|
expect(result.Peer.chatType).toBe(peer.chatType);
|
||||||
|
expect(result.Peer.peerUid).toBe(peer.peerUid);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getMsgIdAndPeerByShortId should return undefined if the shortId does not exist', () => {
|
||||||
|
const peer = new Peer();
|
||||||
|
peer.chatType = 1;
|
||||||
|
peer.peerUid = '123';
|
||||||
|
const msgId = 'msgId123';
|
||||||
|
|
||||||
|
messageUniqueWrapper.createMsg(peer, msgId);
|
||||||
|
|
||||||
|
const invalidShortId = 12345678;
|
||||||
|
const result = messageUniqueWrapper.getMsgIdAndPeerByShortId(invalidShortId);
|
||||||
|
|
||||||
|
expect(result).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getShortIdByMsgId should return the shortId for a given message Id', () => {
|
||||||
|
const peer = new Peer();
|
||||||
|
peer.chatType = 1;
|
||||||
|
peer.peerUid = '123';
|
||||||
|
const msgId = 'msgId123';
|
||||||
|
|
||||||
|
messageUniqueWrapper.createMsg(peer, msgId);
|
||||||
|
|
||||||
|
const shortId = messageUniqueWrapper.getShortIdByMsgId(msgId);
|
||||||
|
|
||||||
|
expect(shortId).toBeDefined();
|
||||||
|
expect(typeof shortId).toBe('number');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getShortIdByMsgId should return undefined if the message Id does not exist', () => {
|
||||||
|
const peer = new Peer();
|
||||||
|
peer.chatType = 1;
|
||||||
|
peer.peerUid = '123';
|
||||||
|
const msgId = 'msgId123';
|
||||||
|
|
||||||
|
messageUniqueWrapper.createMsg(peer, msgId);
|
||||||
|
|
||||||
|
const invalidMsgId = 'invalidMsgId';
|
||||||
|
const shortId = messageUniqueWrapper.getShortIdByMsgId(invalidMsgId);
|
||||||
|
|
||||||
|
expect(shortId).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
@ -1,4 +0,0 @@
|
|||||||
let t = require('./NapCatNative.node');
|
|
||||||
console.log(t);
|
|
||||||
let r = t.ClearElectronAsNode();
|
|
||||||
console.log(r);
|
|
Binary file not shown.
@ -1,6 +0,0 @@
|
|||||||
# Test For NapCatQQ
|
|
||||||
This is a test for NapCatQQ.
|
|
||||||
|
|
||||||
# 计划
|
|
||||||
1. 根据配置文件启动不同的测试 Event与Api
|
|
||||||
2. 标记特殊注意的测试
|
|
@ -1,4 +0,0 @@
|
|||||||
def send_pic_local_msg(user, file):
|
|
||||||
pass
|
|
||||||
def send_pic_http_msg(user, pic_url):
|
|
||||||
pass
|
|
@ -1,6 +0,0 @@
|
|||||||
import requests
|
|
||||||
import pyyaml
|
|
||||||
def __main__():
|
|
||||||
print("TEST")
|
|
||||||
|
|
||||||
__main__()
|
|
@ -1 +0,0 @@
|
|||||||
# todo
|
|
@ -1 +0,0 @@
|
|||||||
#发送消息
|
|
@ -1 +0,0 @@
|
|||||||
pyyaml==5.4.1
|
|
Loading…
x
Reference in New Issue
Block a user