Files
Qt_DesktopPet/internal/qa/tests/ai/ConversationStoreTests.cpp
T

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));
}