mirror of
https://github.com/foss42/apidash.git
synced 2025-12-01 18:28:25 +08:00
tests: add chat model tests(cv: 100)
This commit is contained in:
@@ -18,18 +18,4 @@ class ChatResponse {
|
||||
messageType: messageType ?? this.messageType,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
'ChatResponse(content: $content, messageType: $messageType)';
|
||||
|
||||
@override
|
||||
bool operator ==(covariant ChatResponse other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other.content == content && other.messageType == messageType;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => content.hashCode ^ messageType.hashCode;
|
||||
}
|
||||
|
||||
45
test/dashbot/models/chat_action_model_test.dart
Normal file
45
test/dashbot/models/chat_action_model_test.dart
Normal file
@@ -0,0 +1,45 @@
|
||||
import 'package:apidash/dashbot/core/constants/constants.dart';
|
||||
import 'package:apidash/dashbot/features/chat/models/chat_action.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
group('ChatAction serialization & defaults', () {
|
||||
test('fromJson (full) -> correct enum mapping', () {
|
||||
final json = {
|
||||
'action': 'apply_openapi',
|
||||
'target': 'documentation',
|
||||
'field': 'spec',
|
||||
'path': '/components/schemas',
|
||||
'value': {'k': 'v'},
|
||||
};
|
||||
final action = ChatAction.fromJson(json);
|
||||
expect(action.actionType, ChatActionType.applyOpenApi);
|
||||
expect(action.targetType, ChatActionTarget.documentation);
|
||||
expect(action.field, 'spec');
|
||||
expect(action.path, '/components/schemas');
|
||||
expect(action.value, {'k': 'v'});
|
||||
});
|
||||
|
||||
test('fromJson (missing fields) -> uses safe defaults', () {
|
||||
final action = ChatAction.fromJson({});
|
||||
expect(action.actionType, ChatActionType.other);
|
||||
expect(action.targetType, ChatActionTarget.httpRequestModel);
|
||||
expect(action.field, '');
|
||||
expect(action.path, isNull);
|
||||
});
|
||||
|
||||
test('toJson produces expected keys & enum strings', () {
|
||||
const action = ChatAction(
|
||||
action: 'download_doc',
|
||||
target: 'documentation',
|
||||
actionType: ChatActionType.downloadDoc,
|
||||
targetType: ChatActionTarget.documentation,
|
||||
);
|
||||
final json = action.toJson();
|
||||
expect(json, containsPair('action', 'download_doc'));
|
||||
expect(json, containsPair('target', 'documentation'));
|
||||
expect(json, containsPair('action_type', 'download_doc'));
|
||||
expect(json, containsPair('target_type', 'documentation'));
|
||||
});
|
||||
});
|
||||
}
|
||||
46
test/dashbot/models/chat_attachment_model_test.dart
Normal file
46
test/dashbot/models/chat_attachment_model_test.dart
Normal file
@@ -0,0 +1,46 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:apidash/dashbot/core/model/chat_attachment.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
group('ChatAttachment', () {
|
||||
test('should create model with provided values', () {
|
||||
final data = Uint8List.fromList(List<int>.generate(16, (i) => i));
|
||||
final attachment = ChatAttachment(
|
||||
id: 'att-1',
|
||||
name: 'sample.txt',
|
||||
mimeType: 'text/plain',
|
||||
sizeBytes: data.length,
|
||||
data: data,
|
||||
createdAt: DateTime.utc(2024, 1, 2, 3, 4, 5),
|
||||
);
|
||||
|
||||
expect(attachment.id, 'att-1');
|
||||
expect(attachment.name, 'sample.txt');
|
||||
expect(attachment.mimeType, 'text/plain');
|
||||
expect(attachment.sizeBytes, 16);
|
||||
expect(attachment.data, data);
|
||||
expect(attachment.createdAt, DateTime.utc(2024, 1, 2, 3, 4, 5));
|
||||
});
|
||||
|
||||
test('immutability (fields are final)', () {
|
||||
final data = Uint8List.fromList([1, 2, 3]);
|
||||
final createdAt = DateTime.now();
|
||||
final attachment = ChatAttachment(
|
||||
id: 'id',
|
||||
name: 'a.bin',
|
||||
mimeType: 'application/octet-stream',
|
||||
sizeBytes: 3,
|
||||
data: data,
|
||||
createdAt: createdAt,
|
||||
);
|
||||
|
||||
// Ensuring references are the same (no defensive copy inside constructor)
|
||||
expect(identical(attachment.data, data), true);
|
||||
|
||||
// This test only executes field access to ensure coverage; no setters exist.
|
||||
expect(() => (attachment.id), returnsNormally);
|
||||
});
|
||||
});
|
||||
}
|
||||
11
test/dashbot/models/chat_failure_model_test.dart
Normal file
11
test/dashbot/models/chat_failure_model_test.dart
Normal file
@@ -0,0 +1,11 @@
|
||||
import 'package:apidash/dashbot/core/error/chat_failure.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
group('ChatFailure basics', () {
|
||||
test('toString format', () {
|
||||
const f = ChatFailure('network down', code: '503');
|
||||
expect(f.toString(), 'ChatFailure: network down');
|
||||
});
|
||||
});
|
||||
}
|
||||
65
test/dashbot/models/chat_message_model_test.dart
Normal file
65
test/dashbot/models/chat_message_model_test.dart
Normal file
@@ -0,0 +1,65 @@
|
||||
import 'package:apidash/dashbot/core/constants/constants.dart';
|
||||
import 'package:apidash/dashbot/features/chat/models/chat_action.dart';
|
||||
import 'package:apidash/dashbot/features/chat/models/chat_message.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
group('ChatMessage equality & hashCode', () {
|
||||
final ts = DateTime.utc(2025, 1, 2, 3, 4, 5);
|
||||
const action = ChatAction(
|
||||
action: 'update_field',
|
||||
target: 'httpRequestModel',
|
||||
field: 'url',
|
||||
value: 'https://api.example.dev',
|
||||
actionType: ChatActionType.updateField,
|
||||
targetType: ChatActionTarget.httpRequestModel,
|
||||
);
|
||||
|
||||
test('identical field values -> objects equal & same hashCode', () {
|
||||
final msg1 = ChatMessage(
|
||||
id: 'm1',
|
||||
content: 'Hello',
|
||||
role: MessageRole.user,
|
||||
timestamp: ts,
|
||||
messageType: ChatMessageType.general,
|
||||
actions: const [action],
|
||||
);
|
||||
final msg2 = ChatMessage(
|
||||
id: 'm1',
|
||||
content: 'Hello',
|
||||
role: MessageRole.user,
|
||||
timestamp: ts,
|
||||
messageType: ChatMessageType.general,
|
||||
actions: const [action],
|
||||
);
|
||||
expect(msg1, msg2);
|
||||
expect(msg1.hashCode, msg2.hashCode);
|
||||
expect(msg1.toString(), contains('ChatMessage'));
|
||||
expect(msg1.toString(), contains('m1'));
|
||||
});
|
||||
|
||||
test('different id -> not equal', () {
|
||||
final a = ChatMessage(
|
||||
id: 'a',
|
||||
content: 'Hi',
|
||||
role: MessageRole.user,
|
||||
timestamp: ts,
|
||||
);
|
||||
final b = a.copyWith(id: 'b');
|
||||
expect(a == b, isFalse);
|
||||
});
|
||||
|
||||
test('copyWith returns updated instance only for provided fields', () {
|
||||
final base = ChatMessage(
|
||||
id: 'base',
|
||||
content: 'Original',
|
||||
role: MessageRole.system,
|
||||
timestamp: ts,
|
||||
);
|
||||
final updated = base.copyWith(content: 'Updated');
|
||||
expect(updated.content, 'Updated');
|
||||
expect(updated.id, 'base');
|
||||
expect(updated.role, MessageRole.system);
|
||||
});
|
||||
});
|
||||
}
|
||||
144
test/dashbot/models/chat_models_test.dart
Normal file
144
test/dashbot/models/chat_models_test.dart
Normal file
@@ -0,0 +1,144 @@
|
||||
import 'package:apidash/dashbot/core/constants/constants.dart';
|
||||
import 'package:apidash/dashbot/core/error/chat_failure.dart';
|
||||
import 'package:apidash/dashbot/features/chat/models/chat_action.dart';
|
||||
import 'package:apidash/dashbot/features/chat/models/chat_message.dart';
|
||||
import 'package:apidash/dashbot/features/chat/models/chat_response.dart';
|
||||
import 'package:apidash/dashbot/features/chat/models/chat_state.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
group('ChatMessage & ChatState', () {
|
||||
test('ChatMessage copyWith preserves and overrides fields', () {
|
||||
final ts = DateTime.utc(2024, 5, 6, 7, 8, 9);
|
||||
const action = ChatAction(
|
||||
action: 'update_field',
|
||||
target: 'httpRequestModel',
|
||||
field: 'url',
|
||||
value: 'https://example.com',
|
||||
actionType: ChatActionType.updateField,
|
||||
targetType: ChatActionTarget.httpRequestModel,
|
||||
);
|
||||
|
||||
final m1 = ChatMessage(
|
||||
id: 'm1',
|
||||
content: 'Hello',
|
||||
role: MessageRole.user,
|
||||
timestamp: ts,
|
||||
messageType: ChatMessageType.general,
|
||||
actions: const [action],
|
||||
);
|
||||
|
||||
final m2 = m1.copyWith(content: 'Hi', role: MessageRole.system);
|
||||
expect(m2.id, 'm1');
|
||||
expect(m2.content, 'Hi');
|
||||
expect(m2.role, MessageRole.system);
|
||||
expect(m2.timestamp, ts);
|
||||
expect(m2.messageType, ChatMessageType.general);
|
||||
expect(m2.actions, isNotNull);
|
||||
expect(m2.actions!.length, 1);
|
||||
});
|
||||
|
||||
test('ChatState copyWith', () {
|
||||
final msg = ChatMessage(
|
||||
id: '1',
|
||||
content: 'c',
|
||||
role: MessageRole.user,
|
||||
timestamp: DateTime.fromMillisecondsSinceEpoch(0),
|
||||
);
|
||||
const failure = ChatFailure('net down', code: '500');
|
||||
const state = ChatState();
|
||||
final updated = state.copyWith(
|
||||
chatSessions: {
|
||||
'req1': [msg]
|
||||
},
|
||||
isGenerating: true,
|
||||
currentStreamingResponse: 'streaming..',
|
||||
currentRequestId: 'req1',
|
||||
lastError: failure,
|
||||
);
|
||||
|
||||
expect(updated.chatSessions['req1']!.first, msg);
|
||||
expect(updated.isGenerating, true);
|
||||
expect(updated.currentStreamingResponse, 'streaming..');
|
||||
expect(updated.currentRequestId, 'req1');
|
||||
expect(updated.lastError, failure);
|
||||
|
||||
// unchanged original
|
||||
expect(state.chatSessions, isEmpty);
|
||||
expect(state.isGenerating, false);
|
||||
});
|
||||
|
||||
test('ChatResponse copyWith', () {
|
||||
const r1 =
|
||||
ChatResponse(content: 'Hello', messageType: ChatMessageType.general);
|
||||
final r2 = r1.copyWith(content: 'Hi again');
|
||||
expect(r2.content, 'Hi again');
|
||||
expect(r2.messageType, ChatMessageType.general);
|
||||
final r3 = r2.copyWith(messageType: ChatMessageType.generateCode);
|
||||
expect(r3.messageType, ChatMessageType.generateCode);
|
||||
expect(r3.content, 'Hi again');
|
||||
});
|
||||
});
|
||||
|
||||
group('ChatAction serialization', () {
|
||||
test('fromJson maps to enums correctly', () {
|
||||
final json = {
|
||||
'action': 'apply_curl',
|
||||
'target': 'httpRequestModel',
|
||||
'field': 'body',
|
||||
'path': '/root',
|
||||
'value': '--curl command--',
|
||||
};
|
||||
|
||||
final action = ChatAction.fromJson(json);
|
||||
expect(action.actionType, ChatActionType.applyCurl);
|
||||
expect(action.targetType, ChatActionTarget.httpRequestModel);
|
||||
expect(action.field, 'body');
|
||||
expect(action.path, '/root');
|
||||
expect(action.value, '--curl command--');
|
||||
});
|
||||
|
||||
test('fromJson with unknown values defaults gracefully', () {
|
||||
final action = ChatAction.fromJson({
|
||||
'action': 'some_new_action',
|
||||
'target': 'weird_target',
|
||||
});
|
||||
expect(action.actionType, ChatActionType.other); // unknown -> other
|
||||
expect(action.targetType, ChatActionTarget.httpRequestModel); // default
|
||||
expect(action.field, '');
|
||||
});
|
||||
|
||||
test('toJson returns enum string representations', () {
|
||||
const action = ChatAction(
|
||||
action: 'download_doc',
|
||||
target: 'documentation',
|
||||
field: 'n/a',
|
||||
actionType: ChatActionType.downloadDoc,
|
||||
targetType: ChatActionTarget.documentation,
|
||||
);
|
||||
final json = action.toJson();
|
||||
expect(json['action'], 'download_doc');
|
||||
expect(json['target'], 'documentation');
|
||||
expect(json['action_type'], 'download_doc');
|
||||
expect(json['target_type'], 'documentation');
|
||||
});
|
||||
});
|
||||
|
||||
group('Enum mapping helpers', () {
|
||||
test('chatActionTypeToString covers all values', () {
|
||||
for (final t in ChatActionType.values) {
|
||||
final s = chatActionTypeToString(t);
|
||||
expect(s, isA<String>());
|
||||
expect(s.isNotEmpty, true);
|
||||
}
|
||||
});
|
||||
|
||||
test('chatActionTargetToString covers all values', () {
|
||||
for (final t in ChatActionTarget.values) {
|
||||
final s = chatActionTargetToString(t);
|
||||
expect(s, isA<String>());
|
||||
expect(s.isNotEmpty, true);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
27
test/dashbot/models/chat_response_model_test.dart
Normal file
27
test/dashbot/models/chat_response_model_test.dart
Normal file
@@ -0,0 +1,27 @@
|
||||
import 'package:apidash/dashbot/core/constants/constants.dart';
|
||||
import 'package:apidash/dashbot/features/chat/models/chat_response.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
group('ChatResponse equality & copyWith', () {
|
||||
test('equality & hashCode', () {
|
||||
const r1 =
|
||||
ChatResponse(content: 'Hi', messageType: ChatMessageType.general);
|
||||
const r2 =
|
||||
ChatResponse(content: 'Hi', messageType: ChatMessageType.general);
|
||||
expect(r1, r2);
|
||||
expect(r1.hashCode, r2.hashCode);
|
||||
expect(r1.toString(), contains('ChatResponse'));
|
||||
});
|
||||
|
||||
test('copyWith modifies only provided fields', () {
|
||||
const r1 = ChatResponse(content: 'One');
|
||||
final r2 = r1.copyWith(content: 'Two');
|
||||
expect(r2.content, 'Two');
|
||||
expect(r2.messageType, isNull);
|
||||
final r3 = r2.copyWith(messageType: ChatMessageType.generateCode);
|
||||
expect(r3.messageType, ChatMessageType.generateCode);
|
||||
expect(r3.content, 'Two');
|
||||
});
|
||||
});
|
||||
}
|
||||
59
test/dashbot/models/chat_state_model_test.dart
Normal file
59
test/dashbot/models/chat_state_model_test.dart
Normal file
@@ -0,0 +1,59 @@
|
||||
import 'package:apidash/dashbot/core/error/chat_failure.dart';
|
||||
import 'package:apidash/dashbot/features/chat/models/chat_message.dart';
|
||||
import 'package:apidash/dashbot/features/chat/models/chat_state.dart';
|
||||
import 'package:apidash/dashbot/core/constants/constants.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
group('ChatState copyWith & integrity', () {
|
||||
test('copyWith updates provided fields only', () {
|
||||
final msg = ChatMessage(
|
||||
id: '1',
|
||||
content: 'Ping',
|
||||
role: MessageRole.user,
|
||||
timestamp: DateTime.fromMillisecondsSinceEpoch(0),
|
||||
);
|
||||
const failure = ChatFailure('generic');
|
||||
const base = ChatState();
|
||||
final next = base.copyWith(
|
||||
chatSessions: {
|
||||
'r1': [msg]
|
||||
},
|
||||
isGenerating: true,
|
||||
currentStreamingResponse: 'stream',
|
||||
currentRequestId: 'r1',
|
||||
lastError: failure,
|
||||
);
|
||||
expect(next.chatSessions['r1']!.single, msg);
|
||||
expect(next.isGenerating, true);
|
||||
expect(next.currentStreamingResponse, 'stream');
|
||||
expect(next.currentRequestId, 'r1');
|
||||
expect(next.lastError, failure);
|
||||
// Original untouched
|
||||
expect(base.chatSessions, isEmpty);
|
||||
expect(base.isGenerating, false);
|
||||
|
||||
// Calling copyWith with NO arguments hits fallback (right side of ??)
|
||||
final same = next.copyWith();
|
||||
expect(same.chatSessions, next.chatSessions);
|
||||
expect(same.isGenerating, next.isGenerating);
|
||||
expect(same.currentStreamingResponse, next.currentStreamingResponse);
|
||||
expect(same.currentRequestId, next.currentRequestId);
|
||||
expect(same.lastError, next.lastError);
|
||||
|
||||
// Explicit null parameters should also fall back to existing values
|
||||
final viaNulls = next.copyWith(
|
||||
chatSessions: null,
|
||||
isGenerating: null,
|
||||
currentStreamingResponse: null,
|
||||
currentRequestId: null,
|
||||
lastError: null,
|
||||
);
|
||||
expect(viaNulls.chatSessions, next.chatSessions);
|
||||
expect(viaNulls.isGenerating, next.isGenerating);
|
||||
expect(viaNulls.currentStreamingResponse, next.currentStreamingResponse);
|
||||
expect(viaNulls.currentRequestId, next.currentRequestId);
|
||||
expect(viaNulls.lastError, next.lastError);
|
||||
});
|
||||
});
|
||||
}
|
||||
49
test/dashbot/models/constants_model_test.dart
Normal file
49
test/dashbot/models/constants_model_test.dart
Normal file
@@ -0,0 +1,49 @@
|
||||
import 'package:apidash/dashbot/core/constants/constants.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
group('Constants enum mappings', () {
|
||||
test('ChatActionType round-trip mapping', () {
|
||||
final map = <ChatActionType, String>{
|
||||
ChatActionType.updateField: 'update_field',
|
||||
ChatActionType.addHeader: 'add_header',
|
||||
ChatActionType.updateHeader: 'update_header',
|
||||
ChatActionType.deleteHeader: 'delete_header',
|
||||
ChatActionType.updateBody: 'update_body',
|
||||
ChatActionType.updateUrl: 'update_url',
|
||||
ChatActionType.updateMethod: 'update_method',
|
||||
ChatActionType.showLanguages: 'show_languages',
|
||||
ChatActionType.applyCurl: 'apply_curl',
|
||||
ChatActionType.applyOpenApi: 'apply_openapi',
|
||||
ChatActionType.downloadDoc: 'download_doc',
|
||||
ChatActionType.other: 'other',
|
||||
ChatActionType.noAction: 'no_action',
|
||||
ChatActionType.uploadAsset: 'upload_asset',
|
||||
};
|
||||
map.forEach((enumVal, strVal) {
|
||||
expect(chatActionTypeToString(enumVal), strVal);
|
||||
expect(chatActionTypeFromString(strVal), enumVal);
|
||||
});
|
||||
// unknown
|
||||
expect(chatActionTypeFromString('some_new_type'), ChatActionType.other);
|
||||
});
|
||||
|
||||
test('ChatActionTarget round-trip mapping', () {
|
||||
final map = <ChatActionTarget, String>{
|
||||
ChatActionTarget.httpRequestModel: 'httpRequestModel',
|
||||
ChatActionTarget.codegen: 'codegen',
|
||||
ChatActionTarget.test: 'test',
|
||||
ChatActionTarget.code: 'code',
|
||||
ChatActionTarget.attachment: 'attachment',
|
||||
ChatActionTarget.documentation: 'documentation',
|
||||
};
|
||||
map.forEach((enumVal, strVal) {
|
||||
expect(chatActionTargetToString(enumVal), strVal);
|
||||
expect(chatActionTargetFromString(strVal), enumVal);
|
||||
});
|
||||
// unknown maps to default httpRequestModel
|
||||
expect(chatActionTargetFromString('weird'),
|
||||
ChatActionTarget.httpRequestModel);
|
||||
});
|
||||
});
|
||||
}
|
||||
58
test/dashbot/models/dashbot_window_model_test.dart
Normal file
58
test/dashbot/models/dashbot_window_model_test.dart
Normal file
@@ -0,0 +1,58 @@
|
||||
import 'package:apidash/dashbot/core/model/dashbot_window_model.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
group('DashbotWindowModel', () {
|
||||
test('default values', () {
|
||||
const model = DashbotWindowModel();
|
||||
expect(model.width, 400);
|
||||
expect(model.height, 515);
|
||||
expect(model.right, 50);
|
||||
expect(model.bottom, 100);
|
||||
expect(model.isActive, false);
|
||||
expect(model.isPopped, true);
|
||||
expect(model.isHidden, false);
|
||||
});
|
||||
|
||||
test('copyWith changes only specified fields', () {
|
||||
const original = DashbotWindowModel();
|
||||
final updated = original.copyWith(
|
||||
width: 420,
|
||||
height: 600,
|
||||
isActive: true,
|
||||
isHidden: true,
|
||||
);
|
||||
|
||||
expect(updated.width, 420);
|
||||
expect(updated.height, 600);
|
||||
expect(updated.isActive, true);
|
||||
expect(updated.isHidden, true);
|
||||
|
||||
// Unchanged fields retain original values
|
||||
expect(updated.right, original.right);
|
||||
expect(updated.bottom, original.bottom);
|
||||
expect(updated.isPopped, original.isPopped);
|
||||
|
||||
// Original remains unchanged
|
||||
expect(original.width, 400);
|
||||
expect(original.height, 515);
|
||||
expect(original.isActive, false);
|
||||
expect(original.isHidden, false);
|
||||
});
|
||||
|
||||
test('copyWith chaining', () {
|
||||
const original = DashbotWindowModel();
|
||||
final chained = original
|
||||
.copyWith(width: 410)
|
||||
.copyWith(right: 80, bottom: 120)
|
||||
.copyWith(isPopped: false);
|
||||
|
||||
expect(chained.width, 410);
|
||||
expect(chained.right, 80);
|
||||
expect(chained.bottom, 120);
|
||||
expect(chained.isPopped, false);
|
||||
// untouched
|
||||
expect(chained.height, original.height);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user