tests: add authentication unit tests

This commit is contained in:
Udhay-Adithya
2025-07-07 02:03:47 +05:30
parent 32855fd2f9
commit 8d4eedc21b
9 changed files with 1658 additions and 58 deletions

View File

@@ -0,0 +1,338 @@
import 'package:better_networking/better_networking.dart';
import 'package:test/test.dart';
void main() {
group('Authentication Handling Tests', () {
test(
'given sendHttpRequest when no authentication is provided then it should not throw any error',
() async {
const httpRequestModel = HttpRequestModel(
method: HTTPVerb.get,
url: 'https://api.apidash.dev/users',
);
final result = await sendHttpRequest(
'test-request',
APIType.rest,
null,
httpRequestModel,
);
expect(
result.$1?.request?.url.toString(),
equals('https://api.apidash.dev/users'),
);
},
);
test(
'given handleAuth when no authentication is provided then it should return the same httpRequestModel',
() async {
const httpRequestModel = HttpRequestModel(
method: HTTPVerb.get,
url: 'https://api.apidash.dev/users',
);
final result = await handleAuth(httpRequestModel, null);
expect(result.headers, isNull);
expect(result.url, equals('https://api.apidash.dev/users'));
},
);
test(
'given handleAuth when none authentication type is provided then it should add any headers or throw errors',
() async {
const httpRequestModel = HttpRequestModel(
method: HTTPVerb.get,
url: 'https://api.apidash.dev/users',
);
const authModel = AuthModel(type: APIAuthType.none);
final result = await handleAuth(httpRequestModel, authModel);
expect(result.headers, isEmpty);
expect(result.url, equals('https://api.apidash.dev/users'));
},
);
test(
'given handleAuth when basic authentication fields are provided then it should add an authorization header',
() async {
const httpRequestModel = HttpRequestModel(
method: HTTPVerb.get,
url: 'https://api.apidash.dev/users',
);
const basicAuth = AuthBasicAuthModel(
username: 'testuser',
password: 'testpass',
);
const authModel = AuthModel(type: APIAuthType.basic, basic: basicAuth);
final result = await handleAuth(httpRequestModel, authModel);
expect(result.headers, isNotEmpty);
expect(
result.headers?.any((h) => h.name.toLowerCase() == 'authorization'),
isTrue,
);
expect(result.url, equals('https://api.apidash.dev/users'));
},
);
test(
'given handleAuth when handle bearer authentication fields are provided then it should add an authorization header',
() async {
const httpRequestModel = HttpRequestModel(
method: HTTPVerb.get,
url: 'https://api.apidash.dev/users',
);
const bearerAuth = AuthBearerModel(token: 'bearer-token-123');
const authModel = AuthModel(
type: APIAuthType.bearer,
bearer: bearerAuth,
);
final result = await handleAuth(httpRequestModel, authModel);
expect(result.headers, isNotEmpty);
expect(
result.headers?.any((h) => h.name.toLowerCase() == 'authorization'),
isTrue,
);
expect(result.url, equals('https://api.apidash.dev/users'));
},
);
test(
'given handleAuth when API key authentication fields are provided then it should add an authorization header',
() async {
const httpRequestModel = HttpRequestModel(
method: HTTPVerb.get,
url: 'https://api.apidash.dev/users',
);
const apiKeyAuth = AuthApiKeyModel(
key: 'api-key-123',
location: 'header',
name: 'X-API-Key',
);
const authModel = AuthModel(
type: APIAuthType.apiKey,
apikey: apiKeyAuth,
);
final result = await handleAuth(httpRequestModel, authModel);
expect(result.headers, isNotEmpty);
expect(
result.headers?.any((h) => h.name.toLowerCase() == 'x-api-key'),
isTrue,
);
expect(result.url, equals('https://api.apidash.dev/users'));
},
);
test(
'given handleAuth when API key authentication fields are provided then it should add an authorization query',
() async {
const httpRequestModel = HttpRequestModel(
method: HTTPVerb.get,
url: 'https://api.apidash.dev/users',
);
const apiKeyAuth = AuthApiKeyModel(
key: 'api-key-123',
location: 'query',
name: 'apikey',
);
const authModel = AuthModel(
type: APIAuthType.apiKey,
apikey: apiKeyAuth,
);
final result = await handleAuth(httpRequestModel, authModel);
expect(result.params, isNotEmpty);
expect(result.params?.any((p) => p.name == 'apikey'), isTrue);
expect(result.url, equals('https://api.apidash.dev/users'));
},
);
test(
'given handleAuth when JWT authentication fields are provided then it should add an authorization header',
() async {
const httpRequestModel = HttpRequestModel(
method: HTTPVerb.get,
url: 'https://api.apidash.dev/users',
);
const jwtAuth = AuthJwtModel(
secret: 'jwt-secret',
payload: '{"sub": "1234567890"}',
addTokenTo: 'header',
algorithm: 'HS256',
isSecretBase64Encoded: false,
headerPrefix: 'Bearer',
queryParamKey: 'token',
header: 'Authorization',
);
const authModel = AuthModel(type: APIAuthType.jwt, jwt: jwtAuth);
final result = await handleAuth(httpRequestModel, authModel);
expect(result.headers, isNotEmpty);
expect(
result.headers?.any((h) => h.name.toLowerCase() == 'authorization'),
isTrue,
);
expect(result.url, equals('https://api.apidash.dev/users'));
},
);
test(
'given handleAuth when digest authentication fields are provided then it should add an authorization header',
() async {
const httpRequestModel = HttpRequestModel(
method: HTTPVerb.get,
url: 'https://api.apidash.dev/users',
);
const digestAuth = AuthDigestModel(
username: 'digestuser',
password: 'digestpass',
realm: 'test-realm',
nonce: 'test-nonce',
algorithm: 'MD5',
qop: 'auth',
opaque: 'test-opaque',
);
const authModel = AuthModel(
type: APIAuthType.digest,
digest: digestAuth,
);
final result = await handleAuth(httpRequestModel, authModel);
expect(result.headers, isNotEmpty);
expect(
result.headers?.any((h) => h.name.toLowerCase() == 'authorization'),
isTrue,
);
expect(result.url, equals('https://api.apidash.dev/users'));
},
);
test(
'given handleAuth when multiple headers are provided then it should add an authorization header to the existing headers',
() async {
const httpRequestModel = HttpRequestModel(
method: HTTPVerb.get,
url: 'https://api.apidash.dev/users',
headers: [
NameValueModel(name: 'Content-Type', value: 'application/json'),
NameValueModel(name: 'Accept', value: 'application/json'),
],
);
const bearerAuth = AuthBearerModel(token: 'bearer-token-123');
const authModel = AuthModel(
type: APIAuthType.bearer,
bearer: bearerAuth,
);
final result = await handleAuth(httpRequestModel, authModel);
expect(result.headers, isNotEmpty);
expect(result.headers?.any((h) => h.name == 'Content-Type'), isTrue);
expect(result.headers?.any((h) => h.name == 'Accept'), isTrue);
expect(
result.headers?.any((h) => h.name.toLowerCase() == 'authorization'),
isTrue,
);
expect(result.url, equals('https://api.apidash.dev/users'));
},
);
test(
'given handleAuth when multiple params are provided then it should add it to the existing params',
() async {
const httpRequestModel = HttpRequestModel(
method: HTTPVerb.get,
url: 'https://api.apidash.dev/users',
params: [
NameValueModel(name: 'limit', value: '10'),
NameValueModel(name: 'offset', value: '0'),
],
);
const apiKeyAuth = AuthApiKeyModel(
key: 'api-key-123',
location: 'query',
name: 'apikey',
);
const authModel = AuthModel(
type: APIAuthType.apiKey,
apikey: apiKeyAuth,
);
final result = await handleAuth(httpRequestModel, authModel);
expect(result.params, isNotEmpty);
expect(result.params?.any((p) => p.name == 'limit'), isTrue);
expect(result.params?.any((p) => p.name == 'offset'), isTrue);
expect(result.params?.any((p) => p.name == 'apikey'), isTrue);
expect(result.url, equals('https://api.apidash.dev/users'));
},
);
test(
'given handleAuth when special characters are provided it should not throw an error',
() async {
const httpRequestModel = HttpRequestModel(
method: HTTPVerb.get,
url: 'https://api.apidash.dev/users',
);
const basicAuth = AuthBasicAuthModel(
username: 'user@domain.com',
password: r'P@ssw0rd!@#$%^&*()',
);
const authModel = AuthModel(type: APIAuthType.basic, basic: basicAuth);
final result = await handleAuth(httpRequestModel, authModel);
expect(result.headers, isNotEmpty);
expect(
result.headers?.any((h) => h.name.toLowerCase() == 'authorization'),
isTrue,
);
expect(result.url, equals('https://api.apidash.dev/users'));
},
);
test(
'given handleAuth when no values are provided it should not throw an error',
() async {
const httpRequestModel = HttpRequestModel(
method: HTTPVerb.get,
url: 'https://api.apidash.dev/users',
);
const basicAuth = AuthBasicAuthModel(username: '', password: '');
const authModel = AuthModel(type: APIAuthType.basic, basic: basicAuth);
final result = await handleAuth(httpRequestModel, authModel);
expect(result.headers, isNotEmpty);
expect(
result.headers?.any((h) => h.name.toLowerCase() == 'authorization'),
isTrue,
);
expect(result.url, equals('https://api.apidash.dev/users'));
},
);
});
}

View File

@@ -0,0 +1,340 @@
import 'package:better_networking/better_networking.dart';
import 'package:test/test.dart';
void main() {
group('AuthModel Tests', () {
test('should create AuthModel with none type', () {
const authModel = AuthModel(type: APIAuthType.none);
expect(authModel.type, APIAuthType.none);
expect(authModel.basic, isNull);
expect(authModel.bearer, isNull);
expect(authModel.apikey, isNull);
expect(authModel.jwt, isNull);
expect(authModel.digest, isNull);
});
test('should create AuthModel with basic authentication', () {
const basicAuth = AuthBasicAuthModel(
username: 'testuser',
password: 'testpass',
);
const authModel = AuthModel(
type: APIAuthType.basic,
basic: basicAuth,
);
expect(authModel.type, APIAuthType.basic);
expect(authModel.basic, isNotNull);
expect(authModel.basic?.username, 'testuser');
expect(authModel.basic?.password, 'testpass');
});
test('should create AuthModel with bearer token', () {
const bearerAuth = AuthBearerModel(token: 'bearer-token-123');
const authModel = AuthModel(
type: APIAuthType.bearer,
bearer: bearerAuth,
);
expect(authModel.type, APIAuthType.bearer);
expect(authModel.bearer, isNotNull);
expect(authModel.bearer?.token, 'bearer-token-123');
});
test('should create AuthModel with API key authentication', () {
const apiKeyAuth = AuthApiKeyModel(
key: 'api-key-123',
location: 'header',
name: 'X-API-Key',
);
const authModel = AuthModel(
type: APIAuthType.apiKey,
apikey: apiKeyAuth,
);
expect(authModel.type, APIAuthType.apiKey);
expect(authModel.apikey, isNotNull);
expect(authModel.apikey?.key, 'api-key-123');
expect(authModel.apikey?.location, 'header');
expect(authModel.apikey?.name, 'X-API-Key');
});
test('should create AuthModel with JWT authentication', () {
const jwtAuth = AuthJwtModel(
secret: 'jwt-secret',
payload: '{"sub": "1234567890"}',
addTokenTo: 'header',
algorithm: 'HS256',
isSecretBase64Encoded: false,
headerPrefix: 'Bearer',
queryParamKey: 'token',
header: 'Authorization',
);
const authModel = AuthModel(
type: APIAuthType.jwt,
jwt: jwtAuth,
);
expect(authModel.type, APIAuthType.jwt);
expect(authModel.jwt, isNotNull);
expect(authModel.jwt?.secret, 'jwt-secret');
expect(authModel.jwt?.algorithm, 'HS256');
expect(authModel.jwt?.isSecretBase64Encoded, false);
});
test('should create AuthModel with digest authentication', () {
const digestAuth = AuthDigestModel(
username: 'digestuser',
password: 'digestpass',
realm: 'test-realm',
nonce: 'test-nonce',
algorithm: 'MD5',
qop: 'auth',
opaque: 'test-opaque',
);
const authModel = AuthModel(
type: APIAuthType.digest,
digest: digestAuth,
);
expect(authModel.type, APIAuthType.digest);
expect(authModel.digest, isNotNull);
expect(authModel.digest?.username, 'digestuser');
expect(authModel.digest?.realm, 'test-realm');
expect(authModel.digest?.algorithm, 'MD5');
});
test('should serialize and deserialize AuthModel correctly', () {
const originalModel = AuthModel(
type: APIAuthType.basic,
basic: AuthBasicAuthModel(
username: 'testuser',
password: 'testpass',
),
);
final json = originalModel.toJson();
final deserializedModel = AuthModel.fromJson(json);
expect(deserializedModel.type, originalModel.type);
expect(deserializedModel.basic?.username, originalModel.basic?.username);
expect(deserializedModel.basic?.password, originalModel.basic?.password);
});
test('should handle copyWith for AuthModel', () {
const originalModel = AuthModel(
type: APIAuthType.basic,
basic: AuthBasicAuthModel(
username: 'testuser',
password: 'testpass',
),
);
const newBasicAuth = AuthBasicAuthModel(
username: 'newuser',
password: 'newpass',
);
final copiedModel = originalModel.copyWith(
type: APIAuthType.basic,
basic: newBasicAuth,
);
expect(copiedModel.type, APIAuthType.basic);
expect(copiedModel.basic?.username, 'newuser');
expect(copiedModel.basic?.password, 'newpass');
});
test('should handle API key with default values', () {
const apiKeyAuth = AuthApiKeyModel(key: 'test-key');
expect(apiKeyAuth.key, 'test-key');
expect(apiKeyAuth.location, 'header');
expect(apiKeyAuth.name, 'x-api-key');
});
test('should handle API key with custom values', () {
const apiKeyAuth = AuthApiKeyModel(
key: 'custom-key',
location: 'query',
name: 'api_key',
);
expect(apiKeyAuth.key, 'custom-key');
expect(apiKeyAuth.location, 'query');
expect(apiKeyAuth.name, 'api_key');
});
test('should handle JWT with private key', () {
const jwtAuth = AuthJwtModel(
secret: 'jwt-secret',
privateKey: 'private-key-content',
payload: '{"sub": "1234567890"}',
addTokenTo: 'header',
algorithm: 'RS256',
isSecretBase64Encoded: true,
headerPrefix: 'Bearer',
queryParamKey: 'token',
header: 'Authorization',
);
expect(jwtAuth.secret, 'jwt-secret');
expect(jwtAuth.privateKey, 'private-key-content');
expect(jwtAuth.algorithm, 'RS256');
expect(jwtAuth.isSecretBase64Encoded, true);
});
test('should handle edge cases with empty strings', () {
const basicAuth = AuthBasicAuthModel(
username: '',
password: '',
);
const authModel = AuthModel(
type: APIAuthType.basic,
basic: basicAuth,
);
expect(authModel.basic?.username, '');
expect(authModel.basic?.password, '');
});
test('should handle JSON serialization with null values', () {
const authModel = AuthModel(type: APIAuthType.none);
final json = authModel.toJson();
final deserializedModel = AuthModel.fromJson(json);
expect(deserializedModel.type, APIAuthType.none);
expect(deserializedModel.basic, isNull);
expect(deserializedModel.bearer, isNull);
expect(deserializedModel.apikey, isNull);
expect(deserializedModel.jwt, isNull);
expect(deserializedModel.digest, isNull);
});
test('should handle complex JWT payload', () {
const complexPayload = '''
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022,
"exp": 1516242622,
"roles": ["admin", "user"],
"permissions": {
"read": true,
"write": false
}
}
''';
const jwtAuth = AuthJwtModel(
secret: 'complex-secret',
payload: complexPayload,
addTokenTo: 'header',
algorithm: 'HS512',
isSecretBase64Encoded: false,
headerPrefix: 'JWT',
queryParamKey: 'jwt_token',
header: 'X-JWT-Token',
);
expect(jwtAuth.payload, complexPayload);
expect(jwtAuth.headerPrefix, 'JWT');
expect(jwtAuth.queryParamKey, 'jwt_token');
expect(jwtAuth.header, 'X-JWT-Token');
});
test('should handle digest auth with all parameters', () {
const digestAuth = AuthDigestModel(
username: 'digestuser',
password: 'digestpass',
realm: 'api.example.com',
nonce: 'dcd98b7102dd2f0e8b11d0f600bfb0c093',
algorithm: 'SHA-256',
qop: 'auth-int',
opaque: '5ccc069c403ebaf9f0171e9517f40e41',
);
expect(digestAuth.username, 'digestuser');
expect(digestAuth.password, 'digestpass');
expect(digestAuth.realm, 'api.example.com');
expect(digestAuth.nonce, 'dcd98b7102dd2f0e8b11d0f600bfb0c093');
expect(digestAuth.algorithm, 'SHA-256');
expect(digestAuth.qop, 'auth-int');
expect(digestAuth.opaque, '5ccc069c403ebaf9f0171e9517f40e41');
});
});
test('should handle type mismatch scenarios', () {
// Test when type is basic but bearer data is provided
const authModel = AuthModel(
type: APIAuthType.basic,
bearer: AuthBearerModel(token: 'token'),
);
expect(authModel.type, APIAuthType.basic);
expect(authModel.bearer?.token, 'token');
expect(authModel.basic, isNull);
});
test('should handle multiple auth types provided', () {
const authModel = AuthModel(
type: APIAuthType.bearer,
basic: AuthBasicAuthModel(username: 'user', password: 'pass'),
bearer: AuthBearerModel(token: 'token'),
apikey: AuthApiKeyModel(key: 'key'),
);
expect(authModel.type, APIAuthType.bearer);
expect(authModel.basic, isNotNull);
expect(authModel.bearer, isNotNull);
expect(authModel.apikey, isNotNull);
});
test('should handle serialization with special characters', () {
const basicAuth = AuthBasicAuthModel(
username: 'user@domain.com',
password: r'P@ssw0rd!@#$%^&*()',
);
const authModel = AuthModel(type: APIAuthType.basic, basic: basicAuth);
final json = authModel.toJson();
final deserializedModel = AuthModel.fromJson(json);
expect(deserializedModel.basic?.username, 'user@domain.com');
expect(deserializedModel.basic?.password, r'P@ssw0rd!@#$%^&*()');
});
test('should handle very long strings', () {
final longString = 'a' * 1000;
final bearerAuth = AuthBearerModel(token: longString);
final authModel = AuthModel(type: APIAuthType.bearer, bearer: bearerAuth);
expect(authModel.bearer?.token, longString);
expect(authModel.bearer?.token.length, 1000);
});
test('should handle Unicode characters', () {
const basicAuth = AuthBasicAuthModel(
username: 'user_测试_тест_テスト',
password: 'password_🔑_🚀_💻',
);
const authModel = AuthModel(type: APIAuthType.basic, basic: basicAuth);
final json = authModel.toJson();
final deserializedModel = AuthModel.fromJson(json);
expect(deserializedModel.basic?.username, 'user_测试_тест_テスト');
expect(deserializedModel.basic?.password, 'password_🔑_🚀_💻');
});
}

View File

@@ -9,6 +9,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "82.0.0"
adaptive_number:
dependency: transitive
description:
name: adaptive_number
sha256: "3a567544e9b5c9c803006f51140ad544aedc79604fd4f3f2c1380003f97c1d77"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
analyzer:
dependency: transitive
description:
@@ -349,6 +357,14 @@ packages:
relative: true
source: path
version: "0.1.3"
dart_jsonwebtoken:
dependency: transitive
description:
name: dart_jsonwebtoken
sha256: "21ce9f8a8712f741e8d6876a9c82c0f8a257fe928c4378a91d8527b92a3fd413"
url: "https://pub.dev"
source: hosted
version: "3.2.0"
dart_style:
dependency: "direct main"
description:
@@ -381,6 +397,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.5.0"
ed25519_edwards:
dependency: transitive
description:
name: ed25519_edwards
sha256: "6ce0112d131327ec6d42beede1e5dfd526069b18ad45dcf654f15074ad9276cd"
url: "https://pub.dev"
source: hosted
version: "0.3.1"
equatable:
dependency: transitive
description:
@@ -1246,6 +1270,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.8"
pointycastle:
dependency: transitive
description:
name: pointycastle
sha256: "92aa3841d083cc4b0f4709b5c74fd6409a3e6ba833ffc7dc6a8fee096366acf5"
url: "https://pub.dev"
source: hosted
version: "4.0.0"
pool:
dependency: transitive
description:

View File

@@ -22,7 +22,7 @@ final historyRequestModel1 = HistoryRequestModel(
metaData: historyMetaModel1,
httpRequestModel: httpRequestModelGet4,
httpResponseModel: responseModel,
);
authModel: AuthModel(type: APIAuthType.none));
final historyMetaModel2 = HistoryMetaModel(
historyId: 'historyId2',
@@ -39,7 +39,7 @@ final historyRequestModel2 = HistoryRequestModel(
metaData: historyMetaModel2,
httpRequestModel: httpRequestModelPost10,
httpResponseModel: responseModel,
);
authModel: AuthModel(type: APIAuthType.none));
/// JSONs
final Map<String, dynamic> historyMetaModelJson1 = {
@@ -59,7 +59,15 @@ final Map<String, dynamic> historyRequestModelJson1 = {
"httpRequestModel": httpRequestModelGet4Json,
"httpResponseModel": responseModelJson,
'preRequestScript': null,
'postRequestScript': null
'postRequestScript': null,
'authModel': {
'type': 'none',
'apikey': null,
'bearer': null,
'basic': null,
'jwt': null,
'digest': null
}
};
final Map<String, dynamic> historyMetaModelJson2 = {

View File

@@ -83,4 +83,325 @@ void main() {
var httpRequestModel3 = httpRequestModel.copyWith(headers: null);
expect(httpRequestModel3.headers, null);
});
group('HttpRequestModel Auth Tests', () {
test('should create HttpRequestModel with no authentication', () {
const httpRequestModel = HttpRequestModel(
method: HTTPVerb.get,
url: 'https://api.apidash.dev/users',
);
expect(httpRequestModel.authModel?.type, APIAuthType.none);
});
test('should create HttpRequestModel with basic authentication', () {
const basicAuth = AuthBasicAuthModel(
username: 'testuser',
password: 'testpass',
);
const authModel = AuthModel(
type: APIAuthType.basic,
basic: basicAuth,
);
const httpRequestModel = HttpRequestModel(
method: HTTPVerb.get,
url: 'https://api.apidash.dev/users',
authModel: authModel,
);
expect(httpRequestModel.authModel, isNotNull);
expect(httpRequestModel.authModel?.type, APIAuthType.basic);
expect(httpRequestModel.authModel?.basic?.username, 'testuser');
expect(httpRequestModel.authModel?.basic?.password, 'testpass');
});
test('should create HttpRequestModel with bearer authentication', () {
const bearerAuth = AuthBearerModel(token: 'bearer-token-123');
const authModel = AuthModel(
type: APIAuthType.bearer,
bearer: bearerAuth,
);
const httpRequestModel = HttpRequestModel(
method: HTTPVerb.post,
url: 'https://api.apidash.dev/users',
authModel: authModel,
);
expect(httpRequestModel.authModel?.type, APIAuthType.bearer);
expect(httpRequestModel.authModel?.bearer?.token, 'bearer-token-123');
});
test('should create HttpRequestModel with API key authentication', () {
const apiKeyAuth = AuthApiKeyModel(
key: 'api-key-123',
location: 'header',
name: 'X-API-Key',
);
const authModel = AuthModel(
type: APIAuthType.apiKey,
apikey: apiKeyAuth,
);
const httpRequestModel = HttpRequestModel(
method: HTTPVerb.get,
url: 'https://api.apidash.dev/users',
authModel: authModel,
);
expect(httpRequestModel.authModel?.type, APIAuthType.apiKey);
expect(httpRequestModel.authModel?.apikey?.key, 'api-key-123');
expect(httpRequestModel.authModel?.apikey?.location, 'header');
expect(httpRequestModel.authModel?.apikey?.name, 'X-API-Key');
});
test('should create HttpRequestModel with JWT authentication', () {
const jwtAuth = AuthJwtModel(
secret: 'jwt-secret',
payload: '{"sub": "1234567890"}',
addTokenTo: 'header',
algorithm: 'HS256',
isSecretBase64Encoded: false,
headerPrefix: 'Bearer',
queryParamKey: 'token',
header: 'Authorization',
);
const authModel = AuthModel(
type: APIAuthType.jwt,
jwt: jwtAuth,
);
const httpRequestModel = HttpRequestModel(
method: HTTPVerb.patch,
url: 'https://api.apidash.dev/users/1',
authModel: authModel,
);
expect(httpRequestModel.authModel?.type, APIAuthType.jwt);
expect(httpRequestModel.authModel?.jwt?.secret, 'jwt-secret');
expect(httpRequestModel.authModel?.jwt?.algorithm, 'HS256');
expect(httpRequestModel.authModel?.jwt?.isSecretBase64Encoded, false);
});
test('should create HttpRequestModel with digest authentication', () {
const digestAuth = AuthDigestModel(
username: 'digestuser',
password: 'digestpass',
realm: 'test-realm',
nonce: 'test-nonce',
algorithm: 'MD5',
qop: 'auth',
opaque: 'test-opaque',
);
const authModel = AuthModel(
type: APIAuthType.digest,
digest: digestAuth,
);
const httpRequestModel = HttpRequestModel(
method: HTTPVerb.delete,
url: 'https://api.apidash.dev/users/1',
authModel: authModel,
);
expect(httpRequestModel.authModel?.type, APIAuthType.digest);
expect(httpRequestModel.authModel?.digest?.username, 'digestuser');
expect(httpRequestModel.authModel?.digest?.realm, 'test-realm');
expect(httpRequestModel.authModel?.digest?.algorithm, 'MD5');
});
test(
'should serialize and deserialize HttpRequestModel with auth correctly',
() {
const basicAuth = AuthBasicAuthModel(
username: 'testuser',
password: 'testpass',
);
const authModel = AuthModel(
type: APIAuthType.basic,
basic: basicAuth,
);
const originalModel = HttpRequestModel(
method: HTTPVerb.get,
url: 'https://api.apidash.dev/users',
authModel: authModel,
);
final json = originalModel.toJson();
final deserializedModel = HttpRequestModel.fromJson(json);
expect(deserializedModel.method, originalModel.method);
expect(deserializedModel.url, originalModel.url);
expect(deserializedModel.authModel?.type, originalModel.authModel?.type);
expect(deserializedModel.authModel?.basic?.username,
originalModel.authModel?.basic?.username);
expect(deserializedModel.authModel?.basic?.password,
originalModel.authModel?.basic?.password);
});
test('should handle copyWith for HttpRequestModel with auth', () {
const originalAuth = AuthBasicAuthModel(
username: 'testuser',
password: 'testpass',
);
const originalAuthModel = AuthModel(
type: APIAuthType.basic,
basic: originalAuth,
);
const originalModel = HttpRequestModel(
method: HTTPVerb.get,
url: 'https://api.apidash.dev/users',
authModel: originalAuthModel,
);
const newAuth = AuthBearerModel(token: 'new-bearer-token');
const newAuthModel = AuthModel(
type: APIAuthType.bearer,
bearer: newAuth,
);
final copiedModel = originalModel.copyWith(
authModel: newAuthModel,
);
expect(copiedModel.method, originalModel.method);
expect(copiedModel.url, originalModel.url);
expect(copiedModel.authModel?.type, APIAuthType.bearer);
expect(copiedModel.authModel?.bearer?.token, 'new-bearer-token');
});
test('should handle HttpRequestModel with complex auth scenarios', () {
const complexPayload = '''
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022,
"exp": 1516242622,
"roles": ["admin", "user"],
"permissions": {
"read": true,
"write": false
}
}
''';
const jwtAuth = AuthJwtModel(
secret: 'complex-secret',
privateKey: 'private-key-content',
payload: complexPayload,
addTokenTo: 'query',
algorithm: 'RS256',
isSecretBase64Encoded: true,
headerPrefix: 'JWT',
queryParamKey: 'jwt_token',
header: 'X-JWT-Token',
);
const authModel = AuthModel(
type: APIAuthType.jwt,
jwt: jwtAuth,
);
const httpRequestModel = HttpRequestModel(
method: HTTPVerb.post,
url: 'https://api.apidash.dev/secure-endpoint',
authModel: authModel,
headers: [
NameValueModel(name: 'Content-Type', value: 'application/json'),
NameValueModel(name: 'Accept', value: 'application/json'),
],
);
expect(httpRequestModel.authModel?.jwt?.payload, complexPayload);
expect(
httpRequestModel.authModel?.jwt?.privateKey, 'private-key-content');
expect(httpRequestModel.authModel?.jwt?.algorithm, 'RS256');
expect(httpRequestModel.authModel?.jwt?.isSecretBase64Encoded, true);
expect(httpRequestModel.authModel?.jwt?.addTokenTo, 'query');
});
test('should handle HttpRequestModel with auth and other fields', () {
const apiKeyAuth = AuthApiKeyModel(
key: 'api-key-123',
location: 'header',
name: 'X-API-Key',
);
const authModel = AuthModel(
type: APIAuthType.apiKey,
apikey: apiKeyAuth,
);
const httpRequestModel = HttpRequestModel(
method: HTTPVerb.post,
url: 'https://api.apidash.dev/users',
authModel: authModel,
headers: [
NameValueModel(name: 'Content-Type', value: 'application/json'),
NameValueModel(name: 'Accept', value: 'application/json'),
],
params: [
NameValueModel(name: 'limit', value: '10'),
NameValueModel(name: 'offset', value: '0'),
],
body: '{"name": "John Doe", "email": "john@example.com"}',
bodyContentType: ContentType.json,
);
expect(httpRequestModel.authModel?.type, APIAuthType.apiKey);
expect(httpRequestModel.authModel?.apikey?.key, 'api-key-123');
expect(httpRequestModel.headers?.length, 2);
expect(httpRequestModel.params?.length, 2);
expect(httpRequestModel.body,
'{"name": "John Doe", "email": "john@example.com"}');
expect(httpRequestModel.bodyContentType, ContentType.json);
});
test('should handle HttpRequestModel with multiple auth types in sequence',
() {
const originalAuth = AuthBasicAuthModel(
username: 'testuser',
password: 'testpass',
);
const originalAuthModel = AuthModel(
type: APIAuthType.basic,
basic: originalAuth,
);
var httpRequestModel = HttpRequestModel(
method: HTTPVerb.get,
url: 'https://api.apidash.dev/users',
authModel: originalAuthModel,
);
expect(httpRequestModel.authModel?.type, APIAuthType.basic);
// Change to bearer
const bearerAuth = AuthBearerModel(token: 'bearer-token');
const bearerAuthModel = AuthModel(
type: APIAuthType.bearer,
bearer: bearerAuth,
);
httpRequestModel = httpRequestModel.copyWith(authModel: bearerAuthModel);
expect(httpRequestModel.authModel?.type, APIAuthType.bearer);
// Change to API key
const apiKeyAuth = AuthApiKeyModel(
key: 'api-key',
location: 'query',
name: 'key',
);
const apiKeyAuthModel = AuthModel(
type: APIAuthType.apiKey,
apikey: apiKeyAuth,
);
httpRequestModel = httpRequestModel.copyWith(authModel: apiKeyAuthModel);
expect(httpRequestModel.authModel?.type, APIAuthType.apiKey);
expect(httpRequestModel.authModel?.apikey?.location, 'query');
expect(httpRequestModel.authModel?.apikey?.name, 'key');
});
});
}

View File

@@ -384,6 +384,14 @@ const httpRequestModelGet4Json = <String, dynamic>{
{'name': 'add_space', 'value': 'true'},
{'name': 'trailing_zeros', 'value': 'true'}
],
'authModel': {
'type': 'none',
'apikey': null,
'bearer': null,
'basic': null,
'jwt': null,
'digest': null
},
"isHeaderEnabledList": null,
"isParamEnabledList": null,
"bodyContentType": "json",
@@ -403,6 +411,14 @@ const httpRequestModelPost10Json = <String, dynamic>{
{'name': 'size', 'value': '2'},
{'name': 'len', 'value': '3'}
],
'authModel': {
'type': 'none',
'apikey': null,
'bearer': null,
'basic': null,
'jwt': null,
'digest': null
},
'isHeaderEnabledList': [false, true],
'isParamEnabledList': null,
"bodyContentType": 'json',

View File

@@ -7,7 +7,6 @@ import 'http_response_models.dart';
const requestModelGet1 = RequestModel(
id: 'get1',
apiType: APIType.rest,
authModel: AuthModel(type: APIAuthType.none),
httpRequestModel: httpRequestModelGet1,
);
@@ -15,7 +14,6 @@ const requestModelGet1 = RequestModel(
const requestModelGet2 = RequestModel(
id: 'get2',
apiType: APIType.rest,
authModel: AuthModel(type: APIAuthType.none),
httpRequestModel: httpRequestModelGet2,
);
@@ -23,7 +21,6 @@ const requestModelGet2 = RequestModel(
const requestModelGet3 = RequestModel(
id: 'get3',
apiType: APIType.rest,
authModel: AuthModel(type: APIAuthType.none),
httpRequestModel: httpRequestModelGet3,
);
@@ -31,7 +28,6 @@ const requestModelGet3 = RequestModel(
const requestModelGet4 = RequestModel(
id: 'get4',
apiType: APIType.rest,
authModel: AuthModel(type: APIAuthType.none),
httpRequestModel: httpRequestModelGet4,
);
@@ -39,7 +35,6 @@ const requestModelGet4 = RequestModel(
const requestModelGet5 = RequestModel(
id: 'get5',
apiType: APIType.rest,
authModel: AuthModel(type: APIAuthType.none),
httpRequestModel: httpRequestModelGet5,
);
@@ -47,7 +42,6 @@ const requestModelGet5 = RequestModel(
const requestModelGet6 = RequestModel(
id: 'get6',
apiType: APIType.rest,
authModel: AuthModel(type: APIAuthType.none),
httpRequestModel: httpRequestModelGet6,
);
@@ -55,7 +49,6 @@ const requestModelGet6 = RequestModel(
const requestModelGet7 = RequestModel(
id: 'get7',
apiType: APIType.rest,
authModel: AuthModel(type: APIAuthType.none),
httpRequestModel: httpRequestModelGet7,
);
@@ -63,7 +56,6 @@ const requestModelGet7 = RequestModel(
const requestModelGet8 = RequestModel(
id: 'get8',
apiType: APIType.rest,
authModel: AuthModel(type: APIAuthType.none),
httpRequestModel: httpRequestModelGet8,
);
@@ -71,7 +63,6 @@ const requestModelGet8 = RequestModel(
const requestModelGet9 = RequestModel(
id: 'get9',
apiType: APIType.rest,
authModel: AuthModel(type: APIAuthType.none),
httpRequestModel: httpRequestModelGet9,
);
@@ -79,7 +70,6 @@ const requestModelGet9 = RequestModel(
const requestModelGet10 = RequestModel(
id: 'get10',
apiType: APIType.rest,
authModel: AuthModel(type: APIAuthType.none),
httpRequestModel: httpRequestModelGet10,
);
@@ -87,7 +77,6 @@ const requestModelGet10 = RequestModel(
const requestModelGet11 = RequestModel(
id: 'get11',
apiType: APIType.rest,
authModel: AuthModel(type: APIAuthType.none),
httpRequestModel: httpRequestModelGet11,
);
@@ -95,7 +84,6 @@ const requestModelGet11 = RequestModel(
const requestModelGet12 = RequestModel(
id: 'get12',
apiType: APIType.rest,
authModel: AuthModel(type: APIAuthType.none),
httpRequestModel: httpRequestModelGet12,
);
@@ -103,7 +91,6 @@ const requestModelGet12 = RequestModel(
const requestModelHead1 = RequestModel(
id: 'head1',
apiType: APIType.rest,
authModel: AuthModel(type: APIAuthType.none),
httpRequestModel: httpRequestModelHead1,
);
@@ -111,7 +98,6 @@ const requestModelHead1 = RequestModel(
const requestModelHead2 = RequestModel(
id: 'head2',
apiType: APIType.rest,
authModel: AuthModel(type: APIAuthType.none),
httpRequestModel: httpRequestModelHead2,
);
@@ -119,7 +105,6 @@ const requestModelHead2 = RequestModel(
const requestModelPost1 = RequestModel(
id: 'post1',
apiType: APIType.rest,
authModel: AuthModel(type: APIAuthType.none),
httpRequestModel: httpRequestModelPost1,
);
@@ -127,7 +112,6 @@ const requestModelPost1 = RequestModel(
const requestModelPost2 = RequestModel(
id: 'post2',
apiType: APIType.rest,
authModel: AuthModel(type: APIAuthType.none),
httpRequestModel: httpRequestModelPost2,
);
@@ -135,7 +119,6 @@ const requestModelPost2 = RequestModel(
const requestModelPost3 = RequestModel(
id: 'post3',
apiType: APIType.rest,
authModel: AuthModel(type: APIAuthType.none),
httpRequestModel: httpRequestModelPost3,
);
@@ -143,7 +126,6 @@ const requestModelPost3 = RequestModel(
const requestModelPost4 = RequestModel(
id: 'post4',
apiType: APIType.rest,
authModel: AuthModel(type: APIAuthType.none),
httpRequestModel: httpRequestModelPost4,
);
@@ -151,7 +133,6 @@ const requestModelPost4 = RequestModel(
const requestModelPost5 = RequestModel(
id: 'post5',
apiType: APIType.rest,
authModel: AuthModel(type: APIAuthType.none),
httpRequestModel: httpRequestModelPost5,
);
@@ -159,7 +140,6 @@ const requestModelPost5 = RequestModel(
const requestModelPost6 = RequestModel(
id: 'post6',
apiType: APIType.rest,
authModel: AuthModel(type: APIAuthType.none),
httpRequestModel: httpRequestModelPost6,
);
@@ -167,7 +147,6 @@ const requestModelPost6 = RequestModel(
const requestModelPost7 = RequestModel(
id: 'post7',
apiType: APIType.rest,
authModel: AuthModel(type: APIAuthType.none),
httpRequestModel: httpRequestModelPost7,
);
@@ -175,7 +154,6 @@ const requestModelPost7 = RequestModel(
const requestModelPost8 = RequestModel(
id: 'post8',
apiType: APIType.rest,
authModel: AuthModel(type: APIAuthType.none),
httpRequestModel: httpRequestModelPost8,
);
@@ -183,14 +161,12 @@ const requestModelPost8 = RequestModel(
const requestModelPost9 = RequestModel(
id: 'post9',
apiType: APIType.rest,
authModel: AuthModel(type: APIAuthType.none),
httpRequestModel: httpRequestModelPost9,
);
const requestModelPost10 = RequestModel(
id: 'post9',
apiType: APIType.rest,
authModel: AuthModel(type: APIAuthType.none),
httpRequestModel: httpRequestModelPost10,
);
@@ -198,7 +174,6 @@ const requestModelPost10 = RequestModel(
const requestModelPut1 = RequestModel(
id: 'put1',
apiType: APIType.rest,
authModel: AuthModel(type: APIAuthType.none),
httpRequestModel: httpRequestModelPut1,
);
@@ -206,7 +181,6 @@ const requestModelPut1 = RequestModel(
const requestModelPatch1 = RequestModel(
id: 'patch1',
apiType: APIType.rest,
authModel: AuthModel(type: APIAuthType.none),
httpRequestModel: httpRequestModelPatch1,
);
@@ -214,7 +188,6 @@ const requestModelPatch1 = RequestModel(
const requestModelDelete1 = RequestModel(
id: 'delete1',
apiType: APIType.rest,
authModel: AuthModel(type: APIAuthType.none),
httpRequestModel: httpRequestModelDelete1,
);
@@ -222,7 +195,6 @@ const requestModelDelete1 = RequestModel(
const requestModelDelete2 = RequestModel(
id: 'delete2',
apiType: APIType.rest,
authModel: AuthModel(type: APIAuthType.none),
httpRequestModel: httpRequestModelDelete2,
);
@@ -230,7 +202,6 @@ const requestModelDelete2 = RequestModel(
RequestModel testRequestModel = RequestModel(
id: '1',
apiType: APIType.rest,
authModel: AuthModel(type: APIAuthType.none),
httpRequestModel: httpRequestModelPost10,
responseStatus: 200,
httpResponseModel: responseModel,
@@ -241,7 +212,6 @@ Map<String, dynamic> requestModelJson = {
'id': '1',
'apiType': 'rest',
'name': '',
'authModel': '',
'description': '',
'httpRequestModel': httpRequestModelPost10Json,
'responseStatus': 200,
@@ -255,7 +225,6 @@ Map<String, dynamic> requestModelJson = {
const requestModelGet13 = RequestModel(
id: 'get13',
apiType: APIType.rest,
authModel: AuthModel(type: APIAuthType.none),
httpRequestModel: httpRequestModelGet13,
);
@@ -263,7 +232,6 @@ const requestModelGet13 = RequestModel(
const requestModelGetBadSSL = RequestModel(
id: 'badSSL',
apiType: APIType.rest,
authModel: AuthModel(type: APIAuthType.none),
httpRequestModel: httpRequestModelGetBadSSL,
);
@@ -271,7 +239,6 @@ const requestModelGetBadSSL = RequestModel(
const requestModelPost11 = RequestModel(
id: 'post11',
apiType: APIType.rest,
authModel: AuthModel(type: APIAuthType.none),
httpRequestModel: httpRequestModelPost11,
);
@@ -279,7 +246,6 @@ const requestModelPost11 = RequestModel(
const requestModelPost12 = RequestModel(
id: 'post12',
apiType: APIType.rest,
authModel: AuthModel(type: APIAuthType.none),
httpRequestModel: httpRequestModelPost12,
);
@@ -287,13 +253,11 @@ const requestModelPost12 = RequestModel(
const requestModelPost13 = RequestModel(
id: 'post13',
apiType: APIType.rest,
authModel: AuthModel(type: APIAuthType.none),
httpRequestModel: httpRequestModelPost13,
);
const requestModelOptions1 = RequestModel(
id: 'options1',
apiType: APIType.rest,
authModel: AuthModel(type: APIAuthType.none),
httpRequestModel: httpRequestModelOptions1,
);

View File

@@ -17,7 +17,7 @@ void main() {
var responseRec = await sendHttpRequest(
requestModelGet1.id,
requestModelGet1.apiType,
requestModelGet1.authModel,
AuthModel(type: APIAuthType.none),
requestModelGet1.httpRequestModel!,
defaultUriScheme: kDefaultUriScheme,
noSSL: false,
@@ -36,7 +36,7 @@ void main() {
var responseRec = await sendHttpRequest(
requestModelGet13.id,
requestModelGet13.apiType,
requestModelGet13.authModel,
AuthModel(type: APIAuthType.none),
requestModelGet13.httpRequestModel!,
defaultUriScheme: kDefaultUriScheme,
noSSL: false,
@@ -54,7 +54,7 @@ void main() {
var responseRec = await sendHttpRequest(
requestModelPost11.id,
requestModelPost11.apiType,
requestModelPost11.authModel,
AuthModel(type: APIAuthType.none),
requestModelPost11.httpRequestModel!,
);
@@ -69,7 +69,7 @@ void main() {
var responseRec = await sendHttpRequest(
requestModelPost12.id,
requestModelPost12.apiType,
requestModelPost12.authModel,
AuthModel(type: APIAuthType.none),
requestModelPost12.httpRequestModel!,
);
@@ -83,7 +83,7 @@ void main() {
var responseRec = await sendHttpRequest(
requestModelPost13.id,
requestModelPost13.apiType,
requestModelPost13.authModel,
AuthModel(type: APIAuthType.none),
requestModelPost13.httpRequestModel!,
);
@@ -97,7 +97,7 @@ void main() {
var responseRec = await sendHttpRequest(
requestModelGetBadSSL.id,
requestModelGetBadSSL.apiType,
requestModelGetBadSSL.authModel,
AuthModel(type: APIAuthType.none),
requestModelGetBadSSL.httpRequestModel!,
defaultUriScheme: kDefaultUriScheme,
noSSL: false,
@@ -110,7 +110,7 @@ void main() {
var responseRec = await sendHttpRequest(
requestModelGetBadSSL.id,
requestModelGetBadSSL.apiType,
requestModelGetBadSSL.authModel,
AuthModel(type: APIAuthType.none),
requestModelGetBadSSL.httpRequestModel!,
defaultUriScheme: kDefaultUriScheme,
noSSL: true,
@@ -131,7 +131,7 @@ void main() {
var responseRec = await sendHttpRequest(
requestModelOptions1.id,
requestModelOptions1.apiType,
requestModelOptions1.authModel,
AuthModel(type: APIAuthType.none),
requestModelOptions1.httpRequestModel!,
defaultUriScheme: kDefaultUriScheme,
noSSL: false,
@@ -139,9 +139,14 @@ void main() {
final responseData = responseModel.fromResponse(response: responseRec.$1!);
expect(responseData.statusCode, 200);
expect(responseData.headers?['access-control-allow-methods'], 'GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS');
expect(responseData.headers?['access-control-allow-methods']?.contains("OPTIONS"), true);
expect(responseData.headers?['allow'], 'GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS');
expect(responseData.headers?['access-control-allow-methods'],
'GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS');
expect(
responseData.headers?['access-control-allow-methods']
?.contains("OPTIONS"),
true);
expect(responseData.headers?['allow'],
'GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS');
expect(responseData.headers?['allow']?.contains("OPTIONS"), true);
});
}

View File

@@ -51,4 +51,580 @@ void main() async {
// Verify that the Snackbar is shown
expect(find.text('Switched to POST method'), findsOneWidget);
}, skip: true);
group('CollectionStateNotifier Auth Tests', () {
late ProviderContainer container;
late CollectionStateNotifier notifier;
setUp(() {
container = createContainer();
notifier = container.read(collectionStateNotifierProvider.notifier);
});
test('should update request with basic authentication', () {
final id = notifier.state!.entries.first.key;
const basicAuth = AuthBasicAuthModel(
username: 'testuser',
password: 'testpass',
);
const authModel = AuthModel(
type: APIAuthType.basic,
basic: basicAuth,
);
notifier.update(id: id, authModel: authModel);
final updatedRequest = notifier.getRequestModel(id);
expect(
updatedRequest?.httpRequestModel?.authModel?.type, APIAuthType.basic);
expect(updatedRequest?.httpRequestModel?.authModel?.basic?.username,
'testuser');
expect(updatedRequest?.httpRequestModel?.authModel?.basic?.password,
'testpass');
});
test('should update request with bearer authentication', () {
final id = notifier.state!.entries.first.key;
const bearerAuth = AuthBearerModel(token: 'bearer-token-123');
const authModel = AuthModel(
type: APIAuthType.bearer,
bearer: bearerAuth,
);
notifier.update(id: id, authModel: authModel);
final updatedRequest = notifier.getRequestModel(id);
expect(updatedRequest?.httpRequestModel?.authModel?.type,
APIAuthType.bearer);
expect(updatedRequest?.httpRequestModel?.authModel?.bearer?.token,
'bearer-token-123');
});
test('should update request with API key authentication', () {
final id = notifier.state!.entries.first.key;
const apiKeyAuth = AuthApiKeyModel(
key: 'api-key-123',
location: 'header',
name: 'X-API-Key',
);
const authModel = AuthModel(
type: APIAuthType.apiKey,
apikey: apiKeyAuth,
);
notifier.update(id: id, authModel: authModel);
final updatedRequest = notifier.getRequestModel(id);
expect(updatedRequest?.httpRequestModel?.authModel?.type,
APIAuthType.apiKey);
expect(updatedRequest?.httpRequestModel?.authModel?.apikey?.key,
'api-key-123');
expect(updatedRequest?.httpRequestModel?.authModel?.apikey?.location,
'header');
expect(updatedRequest?.httpRequestModel?.authModel?.apikey?.name,
'X-API-Key');
});
test('should update request with JWT authentication', () {
final id = notifier.state!.entries.first.key;
const jwtAuth = AuthJwtModel(
secret: 'jwt-secret',
payload: '{"sub": "1234567890"}',
addTokenTo: 'header',
algorithm: 'HS256',
isSecretBase64Encoded: false,
headerPrefix: 'Bearer',
queryParamKey: 'token',
header: 'Authorization',
);
const authModel = AuthModel(
type: APIAuthType.jwt,
jwt: jwtAuth,
);
notifier.update(id: id, authModel: authModel);
final updatedRequest = notifier.getRequestModel(id);
expect(
updatedRequest?.httpRequestModel?.authModel?.type, APIAuthType.jwt);
expect(updatedRequest?.httpRequestModel?.authModel?.jwt?.secret,
'jwt-secret');
expect(
updatedRequest?.httpRequestModel?.authModel?.jwt?.algorithm, 'HS256');
expect(
updatedRequest
?.httpRequestModel?.authModel?.jwt?.isSecretBase64Encoded,
false);
});
test('should update request with digest authentication', () {
final id = notifier.state!.entries.first.key;
const digestAuth = AuthDigestModel(
username: 'digestuser',
password: 'digestpass',
realm: 'test-realm',
nonce: 'test-nonce',
algorithm: 'MD5',
qop: 'auth',
opaque: 'test-opaque',
);
const authModel = AuthModel(
type: APIAuthType.digest,
digest: digestAuth,
);
notifier.update(id: id, authModel: authModel);
final updatedRequest = notifier.getRequestModel(id);
expect(updatedRequest?.httpRequestModel?.authModel?.type,
APIAuthType.digest);
expect(updatedRequest?.httpRequestModel?.authModel?.digest?.username,
'digestuser');
expect(updatedRequest?.httpRequestModel?.authModel?.digest?.realm,
'test-realm');
expect(updatedRequest?.httpRequestModel?.authModel?.digest?.algorithm,
'MD5');
});
test('should remove authentication when set to none', () {
final id = notifier.state!.entries.first.key;
// First add auth
const basicAuth = AuthBasicAuthModel(
username: 'testuser',
password: 'testpass',
);
const authModel = AuthModel(
type: APIAuthType.basic,
basic: basicAuth,
);
notifier.update(id: id, authModel: authModel);
// Then remove auth
const noAuthModel = AuthModel(type: APIAuthType.none);
notifier.update(id: id, authModel: noAuthModel);
final updatedRequest = notifier.getRequestModel(id);
expect(
updatedRequest?.httpRequestModel?.authModel?.type, APIAuthType.none);
expect(updatedRequest?.httpRequestModel?.authModel?.basic, isNull);
});
test('should preserve auth when duplicating request', () {
final id = notifier.state!.entries.first.key;
const basicAuth = AuthBasicAuthModel(
username: 'testuser',
password: 'testpass',
);
const authModel = AuthModel(
type: APIAuthType.basic,
basic: basicAuth,
);
notifier.update(id: id, authModel: authModel);
notifier.duplicate(id: id);
final sequence = container.read(requestSequenceProvider);
final duplicatedId = sequence.firstWhere((element) => element != id);
final duplicatedRequest = notifier.getRequestModel(duplicatedId);
expect(duplicatedRequest?.httpRequestModel?.authModel?.type,
APIAuthType.basic);
expect(duplicatedRequest?.httpRequestModel?.authModel?.basic?.username,
'testuser');
expect(duplicatedRequest?.httpRequestModel?.authModel?.basic?.password,
'testpass');
});
test('should not clear auth when clearing response', () {
final id = notifier.state!.entries.first.key;
const bearerAuth = AuthBearerModel(token: 'bearer-token-123');
const authModel = AuthModel(
type: APIAuthType.bearer,
bearer: bearerAuth,
);
notifier.update(id: id, authModel: authModel);
notifier.clearResponse(id: id);
final updatedRequest = notifier.getRequestModel(id);
// Auth should be preserved when clearing response
expect(updatedRequest?.httpRequestModel?.authModel?.type,
APIAuthType.bearer);
expect(updatedRequest?.httpRequestModel?.authModel?.bearer?.token,
'bearer-token-123');
});
test('should handle auth with special characters', () {
final id = notifier.state!.entries.first.key;
const basicAuth = AuthBasicAuthModel(
username: 'user@domain.com',
password: r'P@ssw0rd!@#$%^&*()',
);
const authModel = AuthModel(
type: APIAuthType.basic,
basic: basicAuth,
);
notifier.update(id: id, authModel: authModel);
final updatedRequest = notifier.getRequestModel(id);
expect(updatedRequest?.httpRequestModel?.authModel?.basic?.username,
'user@domain.com');
expect(updatedRequest?.httpRequestModel?.authModel?.basic?.password,
r'P@ssw0rd!@#$%^&*()');
});
test('should handle multiple auth type changes', () {
final id = notifier.state!.entries.first.key;
// Start with basic auth
const basicAuth = AuthBasicAuthModel(
username: 'testuser',
password: 'testpass',
);
const basicAuthModel = AuthModel(
type: APIAuthType.basic,
basic: basicAuth,
);
notifier.update(id: id, authModel: basicAuthModel);
// Switch to bearer
const bearerAuth = AuthBearerModel(token: 'bearer-token-123');
const bearerAuthModel = AuthModel(
type: APIAuthType.bearer,
bearer: bearerAuth,
);
notifier.update(id: id, authModel: bearerAuthModel);
// Switch to API key
const apiKeyAuth = AuthApiKeyModel(
key: 'api-key-123',
location: 'query',
name: 'apikey',
);
const apiKeyAuthModel = AuthModel(
type: APIAuthType.apiKey,
apikey: apiKeyAuth,
);
notifier.update(id: id, authModel: apiKeyAuthModel);
final updatedRequest = notifier.getRequestModel(id);
expect(updatedRequest?.httpRequestModel?.authModel?.type,
APIAuthType.apiKey);
expect(updatedRequest?.httpRequestModel?.authModel?.apikey?.key,
'api-key-123');
expect(updatedRequest?.httpRequestModel?.authModel?.apikey?.location,
'query');
expect(
updatedRequest?.httpRequestModel?.authModel?.apikey?.name, 'apikey');
});
test('should handle empty auth values', () {
final id = notifier.state!.entries.first.key;
const basicAuth = AuthBasicAuthModel(
username: '',
password: '',
);
const authModel = AuthModel(
type: APIAuthType.basic,
basic: basicAuth,
);
notifier.update(id: id, authModel: authModel);
final updatedRequest = notifier.getRequestModel(id);
expect(
updatedRequest?.httpRequestModel?.authModel?.type, APIAuthType.basic);
expect(updatedRequest?.httpRequestModel?.authModel?.basic?.username, '');
expect(updatedRequest?.httpRequestModel?.authModel?.basic?.password, '');
});
test('should save and load auth data correctly', () async {
final notifier = container.read(collectionStateNotifierProvider.notifier);
final id = notifier.state!.entries.first.key;
const jwtAuth = AuthJwtModel(
secret: 'jwt-secret',
payload: '{"sub": "1234567890"}',
addTokenTo: 'header',
algorithm: 'HS256',
isSecretBase64Encoded: false,
headerPrefix: 'Bearer',
queryParamKey: 'token',
header: 'Authorization',
);
const authModel = AuthModel(
type: APIAuthType.jwt,
jwt: jwtAuth,
);
notifier.update(id: id, authModel: authModel);
await notifier.saveData();
// Create new container and load data
late ProviderContainer newContainer;
try {
newContainer = ProviderContainer();
// Wait for the container to initialize by accessing the provider
final newNotifier =
newContainer.read(collectionStateNotifierProvider.notifier);
// Give some time for the microtask in the constructor to complete
await Future.delayed(const Duration(milliseconds: 10));
final loadedRequest = newNotifier.getRequestModel(id);
expect(
loadedRequest?.httpRequestModel?.authModel?.type, APIAuthType.jwt);
expect(loadedRequest?.httpRequestModel?.authModel?.jwt?.secret,
'jwt-secret');
expect(loadedRequest?.httpRequestModel?.authModel?.jwt?.algorithm,
'HS256');
} finally {
newContainer.dispose();
}
});
test('should handle auth in addRequestModel', () {
const basicAuth = AuthBasicAuthModel(
username: 'testuser',
password: 'testpass',
);
const authModel = AuthModel(
type: APIAuthType.basic,
basic: basicAuth,
);
final httpRequestModel = HttpRequestModel(
method: HTTPVerb.get,
url: 'https://api.example.com/users',
authModel: authModel,
);
notifier.addRequestModel(httpRequestModel, name: 'Test Request');
final sequence = container.read(requestSequenceProvider);
final addedRequest = notifier.getRequestModel(sequence.first);
expect(
addedRequest?.httpRequestModel?.authModel?.type, APIAuthType.basic);
expect(addedRequest?.httpRequestModel?.authModel?.basic?.username,
'testuser');
expect(addedRequest?.httpRequestModel?.authModel?.basic?.password,
'testpass');
});
test('should handle complex JWT configuration', () {
final id = notifier.state!.entries.first.key;
const complexPayload = '''
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022,
"exp": 1516242622,
"roles": ["admin", "user"],
"permissions": {
"read": true,
"write": false
}
}
''';
const jwtAuth = AuthJwtModel(
secret: 'complex-secret',
privateKey: 'private-key-content',
payload: complexPayload,
addTokenTo: 'query',
algorithm: 'RS256',
isSecretBase64Encoded: true,
headerPrefix: 'JWT',
queryParamKey: 'jwt_token',
header: 'X-JWT-Token',
);
const authModel = AuthModel(
type: APIAuthType.jwt,
jwt: jwtAuth,
);
notifier.update(id: id, authModel: authModel);
final updatedRequest = notifier.getRequestModel(id);
expect(
updatedRequest?.httpRequestModel?.authModel?.type, APIAuthType.jwt);
expect(updatedRequest?.httpRequestModel?.authModel?.jwt?.payload,
complexPayload);
expect(updatedRequest?.httpRequestModel?.authModel?.jwt?.privateKey,
'private-key-content');
expect(
updatedRequest?.httpRequestModel?.authModel?.jwt?.algorithm, 'RS256');
expect(
updatedRequest
?.httpRequestModel?.authModel?.jwt?.isSecretBase64Encoded,
true);
expect(updatedRequest?.httpRequestModel?.authModel?.jwt?.addTokenTo,
'query');
});
test('should handle API key in different locations', () {
final id = notifier.state!.entries.first.key;
// Test header location
const headerApiKey = AuthApiKeyModel(
key: 'header-key',
location: 'header',
name: 'X-API-Key',
);
const headerAuthModel = AuthModel(
type: APIAuthType.apiKey,
apikey: headerApiKey,
);
notifier.update(id: id, authModel: headerAuthModel);
var updatedRequest = notifier.getRequestModel(id);
expect(updatedRequest?.httpRequestModel?.authModel?.apikey?.location,
'header');
expect(updatedRequest?.httpRequestModel?.authModel?.apikey?.name,
'X-API-Key');
// Test query location
const queryApiKey = AuthApiKeyModel(
key: 'query-key',
location: 'query',
name: 'apikey',
);
const queryAuthModel = AuthModel(
type: APIAuthType.apiKey,
apikey: queryApiKey,
);
notifier.update(id: id, authModel: queryAuthModel);
updatedRequest = notifier.getRequestModel(id);
expect(updatedRequest?.httpRequestModel?.authModel?.apikey?.location,
'query');
expect(
updatedRequest?.httpRequestModel?.authModel?.apikey?.name, 'apikey');
});
test('should handle digest auth with different algorithms', () {
final id = notifier.state!.entries.first.key;
// Test MD5 algorithm
const md5DigestAuth = AuthDigestModel(
username: 'digestuser',
password: 'digestpass',
realm: 'test-realm',
nonce: 'test-nonce',
algorithm: 'MD5',
qop: 'auth',
opaque: 'test-opaque',
);
const md5AuthModel = AuthModel(
type: APIAuthType.digest,
digest: md5DigestAuth,
);
notifier.update(id: id, authModel: md5AuthModel);
var updatedRequest = notifier.getRequestModel(id);
expect(updatedRequest?.httpRequestModel?.authModel?.digest?.algorithm,
'MD5');
// Test SHA-256 algorithm
const sha256DigestAuth = AuthDigestModel(
username: 'digestuser',
password: 'digestpass',
realm: 'test-realm',
nonce: 'test-nonce',
algorithm: 'SHA-256',
qop: 'auth-int',
opaque: 'test-opaque',
);
const sha256AuthModel = AuthModel(
type: APIAuthType.digest,
digest: sha256DigestAuth,
);
notifier.update(id: id, authModel: sha256AuthModel);
updatedRequest = notifier.getRequestModel(id);
expect(updatedRequest?.httpRequestModel?.authModel?.digest?.algorithm,
'SHA-256');
expect(
updatedRequest?.httpRequestModel?.authModel?.digest?.qop, 'auth-int');
});
test('should handle auth model copyWith functionality', () {
final id = notifier.state!.entries.first.key;
const originalAuth = AuthBasicAuthModel(
username: 'original',
password: 'original',
);
const originalAuthModel = AuthModel(
type: APIAuthType.basic,
basic: originalAuth,
);
notifier.update(id: id, authModel: originalAuthModel);
// Update with copyWith
const updatedAuth = AuthBasicAuthModel(
username: 'updated',
password: 'updated',
);
final updatedAuthModel = originalAuthModel.copyWith(
basic: updatedAuth,
);
notifier.update(id: id, authModel: updatedAuthModel);
final updatedRequest = notifier.getRequestModel(id);
expect(updatedRequest?.httpRequestModel?.authModel?.basic?.username,
'updated');
expect(updatedRequest?.httpRequestModel?.authModel?.basic?.password,
'updated');
});
test('should handle auth with very long tokens', () {
final id = notifier.state!.entries.first.key;
final longToken = 'a' * 5000; // Very long token
final bearerAuth = AuthBearerModel(token: longToken);
final authModel = AuthModel(
type: APIAuthType.bearer,
bearer: bearerAuth,
);
notifier.update(id: id, authModel: authModel);
final updatedRequest = notifier.getRequestModel(id);
expect(updatedRequest?.httpRequestModel?.authModel?.bearer?.token,
longToken);
expect(updatedRequest?.httpRequestModel?.authModel?.bearer?.token.length,
5000);
});
test('should handle auth with Unicode characters', () {
final id = notifier.state!.entries.first.key;
const basicAuth = AuthBasicAuthModel(
username: 'user_测试_тест_テスト',
password: 'password_🔑_🚀_💻',
);
const authModel = AuthModel(
type: APIAuthType.basic,
basic: basicAuth,
);
notifier.update(id: id, authModel: authModel);
final updatedRequest = notifier.getRequestModel(id);
expect(updatedRequest?.httpRequestModel?.authModel?.basic?.username,
'user_测试_тест_テスト');
expect(updatedRequest?.httpRequestModel?.authModel?.basic?.password,
'password_🔑_🚀_💻');
});
tearDown(() {
container.dispose();
});
});
}