119 lines
4.0 KiB
C++
119 lines
4.0 KiB
C++
#include "TestHarness.h"
|
|
|
|
#include "src/ai/ConversationStore.h"
|
|
|
|
#include <QByteArray>
|
|
#include <QDate>
|
|
#include <QDateTime>
|
|
#include <QDir>
|
|
#include <QFile>
|
|
#include <QJsonArray>
|
|
#include <QJsonDocument>
|
|
#include <QJsonObject>
|
|
#include <QTemporaryDir>
|
|
#include <QTime>
|
|
|
|
namespace
|
|
{
|
|
ChatMessage message(const QString &role, const QString &content, int minute)
|
|
{
|
|
return {
|
|
role,
|
|
content,
|
|
QDateTime(QDate(2026, 7, 17), QTime(10, minute)),
|
|
QStringLiteral("openai"),
|
|
QStringLiteral("test-model"),
|
|
};
|
|
}
|
|
|
|
bool writeBytes(const QString &filePath, const QByteArray &bytes)
|
|
{
|
|
QFile file(filePath);
|
|
return file.open(QIODevice::WriteOnly | QIODevice::Truncate)
|
|
&& file.write(bytes) == bytes.size();
|
|
}
|
|
}
|
|
|
|
QA_TEST(ConversationStore, SavesLoadsAndLimitsEvenHistory)
|
|
{
|
|
QTemporaryDir directory;
|
|
QA_EXPECT(directory.isValid());
|
|
const QString filePath = directory.filePath(QStringLiteral("conversation_history.json"));
|
|
const ConversationStore store(filePath);
|
|
const QVector<ChatMessage> messages = {
|
|
message(QStringLiteral("user"), QStringLiteral("one"), 0),
|
|
message(QStringLiteral("assistant"), QStringLiteral("two"), 1),
|
|
message(QStringLiteral("user"), QStringLiteral("three"), 2),
|
|
message(QStringLiteral("assistant"), QStringLiteral("four"), 3),
|
|
};
|
|
|
|
QA_EXPECT(store.save(messages, 4));
|
|
const QVector<ChatMessage> loaded = store.load(3);
|
|
QA_EXPECT_EQ(loaded.size(), 2);
|
|
QA_EXPECT_EQ(loaded.first().content, QStringLiteral("three"));
|
|
QA_EXPECT_EQ(loaded.last().model, QStringLiteral("test-model"));
|
|
}
|
|
|
|
QA_TEST(ConversationStore, AvoidsDuplicatingOverlappingMessages)
|
|
{
|
|
QTemporaryDir directory;
|
|
QA_EXPECT(directory.isValid());
|
|
const ConversationStore store(directory.filePath(QStringLiteral("history.json")));
|
|
const QVector<ChatMessage> firstPair = {
|
|
message(QStringLiteral("user"), QStringLiteral("hello"), 0),
|
|
message(QStringLiteral("assistant"), QStringLiteral("world"), 1),
|
|
};
|
|
|
|
QA_EXPECT(store.save(firstPair, 10));
|
|
QA_EXPECT(store.save(firstPair, 10));
|
|
QA_EXPECT_EQ(store.load(10).size(), 2);
|
|
}
|
|
|
|
QA_TEST(ConversationStore, LoadsLegacyCreatedAtField)
|
|
{
|
|
QTemporaryDir directory;
|
|
QA_EXPECT(directory.isValid());
|
|
const QString filePath = directory.filePath(QStringLiteral("legacy.json"));
|
|
QJsonArray messages;
|
|
messages.append(QJsonObject{
|
|
{QStringLiteral("role"), QStringLiteral("user")},
|
|
{QStringLiteral("content"), QStringLiteral("legacy-user")},
|
|
{QStringLiteral("createdAt"), QStringLiteral("2026-07-17T10:00:00")},
|
|
});
|
|
messages.append(QJsonObject{
|
|
{QStringLiteral("role"), QStringLiteral("assistant")},
|
|
{QStringLiteral("content"), QStringLiteral("legacy-assistant")},
|
|
{QStringLiteral("createdAt"), QStringLiteral("2026-07-17T10:00:01")},
|
|
});
|
|
QA_EXPECT(writeBytes(filePath, QJsonDocument(QJsonObject{{QStringLiteral("messages"), messages}}).toJson()));
|
|
|
|
const QVector<ChatMessage> loaded = ConversationStore(filePath).load(10);
|
|
QA_EXPECT_EQ(loaded.size(), 2);
|
|
QA_EXPECT(loaded.first().timestamp.isValid());
|
|
QA_EXPECT(loaded.first().provider.isEmpty());
|
|
}
|
|
|
|
QA_TEST(ConversationStore, BacksUpBrokenJsonAndClearsHistory)
|
|
{
|
|
QTemporaryDir directory;
|
|
QA_EXPECT(directory.isValid());
|
|
const QString filePath = directory.filePath(QStringLiteral("conversation_history.json"));
|
|
QA_EXPECT(writeBytes(filePath, QByteArrayLiteral("{broken")));
|
|
|
|
QString errorMessage;
|
|
const ConversationStore store(filePath);
|
|
QA_EXPECT(store.load(10, &errorMessage).isEmpty());
|
|
QA_EXPECT(!errorMessage.isEmpty());
|
|
QA_EXPECT(!QFile::exists(filePath));
|
|
QA_EXPECT_EQ(
|
|
QDir(directory.path()).entryList({QStringLiteral("conversation_history.broken.*.json")}, QDir::Files).size(),
|
|
1);
|
|
|
|
QA_EXPECT(store.save({
|
|
message(QStringLiteral("user"), QStringLiteral("hello"), 0),
|
|
message(QStringLiteral("assistant"), QStringLiteral("world"), 1),
|
|
}, 10));
|
|
QA_EXPECT(store.clear());
|
|
QA_EXPECT(!QFile::exists(filePath));
|
|
}
|