Skip to content

Commit

Permalink
fix: Streamline token errors (#968)
Browse files Browse the repository at this point in the history
Removes specific errors like "too short" or "too long". Also, removes
any error message when the token field is cleared. This change also
streamlines some of the tests so we test multiple cases of different
kinds of invalid tokens, but now each results in the same error.

---

UDENG-5285
  • Loading branch information
ashuntu authored Nov 21, 2024
2 parents 4ddcbb6 + b4263a7 commit 3784bac
Show file tree
Hide file tree
Showing 9 changed files with 64 additions and 81 deletions.
20 changes: 7 additions & 13 deletions gui/packages/ubuntupro/lib/core/pro_token.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,20 @@ import 'package:flutter/material.dart';
import 'base58_check.dart';

/// The possible errors when parsing a candidate Pro token string.
enum TokenError { empty, tooShort, tooLong, invalidPrefix, invalidEncoding }
enum TokenError { empty, invalid }

final _b58 = Base58();

TokenError? _validate(String? value) {
if (value == null || value.isEmpty) {
return TokenError.empty;
}
if (value.length < ProToken.minLength) {
return TokenError.tooShort;
}
if (value.length > ProToken.maxLength) {
return TokenError.tooLong;
}
// For now only Contract tokens are expected.
if (value[0] != 'C') {
return TokenError.invalidPrefix;
}
if (_b58.checkDecode(value.substring(1, value.length)) != null) {
return TokenError.invalidEncoding;

if (value.length < ProToken.minLength ||
value.length > ProToken.maxLength ||
value[0] != 'C' ||
_b58.checkDecode(value.substring(1, value.length)) != null) {
return TokenError.invalid;
}

return null;
Expand Down
5 changes: 1 addition & 4 deletions gui/packages/ubuntupro/lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
{
"appTitle": "Ubuntu Pro for WSL",
"tokenErrorEmpty": "Token cannot be empty",
"tokenErrorTooShort": "Token is too short",
"tokenErrorTooLong": "Token is too long",
"tokenErrorInvalidPrefix": "Token prefix is invalid",
"tokenErrorInvalidEncoding": "Token is corrupted",
"tokenErrorInvalid": "Token invalid",
"tokenInputTitle": "Already have a token?",
"tokenInputHint": "Paste your Ubuntu Pro token here",
"confirm": "Confirm",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,20 +129,14 @@ class ProTokenValue extends EitherValueNotifier<TokenError, ProToken?> {

extension TokenErrorl10n on TokenError {
/// Allows representing the [TokenError] enum as a String.
String localize(AppLocalizations lang) {
String? localize(AppLocalizations lang) {
switch (this) {
case TokenError.empty:
return lang.tokenErrorEmpty;
case TokenError.tooShort:
return lang.tokenErrorTooShort;
case TokenError.tooLong:
return lang.tokenErrorTooLong;
case TokenError.invalidPrefix:
return lang.tokenErrorInvalidPrefix;
case TokenError.invalidEncoding:
return lang.tokenErrorInvalidEncoding;
default:
throw UnimplementedError(toString());
// empty cannot be submitted, but we don't need to display an error to
// the user, just return to original state
return null;
case TokenError.invalid:
return lang.tokenErrorInvalid;
}
}
}
24 changes: 9 additions & 15 deletions gui/packages/ubuntupro/test/core/pro_token_test.dart
Original file line number Diff line number Diff line change
@@ -1,27 +1,21 @@
import 'package:dart_either/dart_either.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:ubuntupro/core/pro_token.dart';
import '../utils/token_samples.dart' as tks;

void main() {
test('token errors', () async {
var token = ProToken.create('');
expect(token, const Left(TokenError.empty));
final proToken = ProToken.create('');
expect(proToken, const Left(TokenError.empty));

token = ProToken.create('ZmNb8uQn5zv');
expect(token, const Left(TokenError.tooShort));

token = ProToken.create('K2RYDcKfupxwXdWhSAxQPCeiULntKm63UXyx5MvEH2');
expect(token, const Left(TokenError.tooLong));

token = ProToken.create('K2RYDcKfupxwXdWhSAxQPCeiULntKm');
expect(token, const Left(TokenError.invalidPrefix));

token = ProToken.create('CK2RYDcKfupxwXdWhSAxQPCeiULntK');
expect(token, const Left(TokenError.invalidEncoding));
for (final token in tks.invalidTokens) {
final proToken = ProToken.create(token);
expect(proToken, const Left(TokenError.invalid));
}
});

test('simulates a valid token', () async {
final token = ProToken.create('CJd8MMN8wXSWsv7wJT8c8dDK');
expect(token.isRight, isTrue);
final proToken = ProToken.create(tks.good);
expect(proToken.isRight, isTrue);
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import 'package:ubuntupro/core/agent_api_client.dart';
import 'package:ubuntupro/core/pro_token.dart';
import 'package:ubuntupro/pages/subscribe_now/subscribe_now_model.dart';

import '../../utils/token_samples.dart' as tks;
import 'subscribe_now_model_test.mocks.dart';
import 'token_samples.dart' as tks;

@GenerateMocks([AgentApiClient])
void main() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import 'package:ubuntupro/core/agent_api_client.dart';
import 'package:ubuntupro/pages/subscribe_now/subscribe_now_model.dart';
import 'package:ubuntupro/pages/subscribe_now/subscribe_now_page.dart';
import 'package:wizard_router/wizard_router.dart';

import '../../utils/build_multiprovider_app.dart';
import '../../utils/token_samples.dart' as tks;
import 'subscribe_now_page_test.mocks.dart';

@GenerateMocks([SubscribeNowModel])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,20 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:ubuntupro/core/pro_token.dart';
import 'package:ubuntupro/pages/subscribe_now/subscribe_now_widgets.dart';
import 'token_samples.dart' as tks;
import '../../utils/token_samples.dart' as tks;

void main() {
group('pro token value', () {
test('errors', () async {
final value = ProTokenValue();

value.update('');

expect(value.errorOrNull, TokenError.empty);

value.update(tks.tooShort);

expect(value.errorOrNull, TokenError.tooShort);

value.update(tks.tooLong);

expect(value.errorOrNull, TokenError.tooLong);

value.update(tks.invalidPrefix);

expect(value.errorOrNull, TokenError.invalidPrefix);

value.update(tks.invalidEncoding);

expect(value.errorOrNull, TokenError.invalidEncoding);
for (final token in tks.invalidTokens) {
value.update(token);
expect(value.errorOrNull, TokenError.invalid);
}
});
test('accessors on success', () {
final value = ProTokenValue();
Expand Down Expand Up @@ -95,36 +83,48 @@ void main() {
expect(button.enabled, isFalse);
});

testWidgets('too short token', (tester) async {
testWidgets('invalid non-empty tokens', (tester) async {
await tester.pumpWidget(theApp);
final inputField = find.byType(TextField);
final context = tester.element(inputField);
final lang = AppLocalizations.of(context);

await tester.enterText(inputField, tks.tooShort);
await tester.pump();
for (final token in tks.invalidTokens) {
await tester.enterText(inputField, token);
await tester.pump();

final input = tester.firstWidget<TextField>(inputField);
expect(input.decoration!.errorText, isNotNull);
expect(input.decoration!.errorText, contains('too short'));
final input = tester.firstWidget<TextField>(inputField);
expect(input.decoration!.errorText, equals(lang.tokenErrorInvalid));

final button =
tester.firstWidget<ElevatedButton>(find.byType(ElevatedButton));
expect(button.enabled, isFalse);
final button =
tester.firstWidget<ElevatedButton>(find.byType(ElevatedButton));
expect(button.enabled, isFalse);
}
});

testWidgets('too long token', (tester) async {
testWidgets('empty token', (tester) async {
// same as above test...
await tester.pumpWidget(theApp);
final inputField = find.byType(TextField);
final context = tester.element(inputField);
final lang = AppLocalizations.of(context);

await tester.enterText(inputField, tks.tooLong);
await tester.enterText(inputField, tks.invalidTokens[0]);
await tester.pump();

final input = tester.firstWidget<TextField>(inputField);
expect(input.decoration!.errorText, isNotNull);
expect(input.decoration!.errorText, contains('too long'));
var input = tester.firstWidget<TextField>(inputField);
expect(input.decoration!.errorText, equals(lang.tokenErrorInvalid));

final button =
tester.firstWidget<ElevatedButton>(find.byType(ElevatedButton));
expect(button.enabled, isFalse);

// ...except when we delete the content we should have no more errors
await tester.enterText(inputField, '');
await tester.pump();
input = tester.firstWidget<TextField>(inputField);
expect(input.decoration!.errorText, isNull);
expect(button.enabled, isFalse);
});

testWidgets('good token', (tester) async {
Expand Down

This file was deleted.

7 changes: 7 additions & 0 deletions gui/packages/ubuntupro/test/utils/token_samples.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const good = 'CJd8MMN8wXSWsv7wJT8c8dDK';
const invalidTokens = [
'CK2RYDcKfup', // too short
'CK2RYDcKfupxwXdWhSAxQPCeiULntK63UXyx5MvEH2', // too long
'K2RYDcKfupxwXdWhSAxQPCeiULntKm', // invalid prefix
'CK2RYDcKfupxwXdWhSAxQPCeiULntK', // invalid encoding
];

0 comments on commit 3784bac

Please sign in to comment.