Initial commit: TDD Workshop exercises for participants

- Password Validator kata with starter code and tests
- Shopping Cart kata with starter code and tests
- FizzBuzz reference code (from live demo)
- Setup guide and TDD reference card
- No solutions included (participants implement themselves)
This commit is contained in:
fiatcode 2026-03-10 15:37:58 +07:00
commit 3d94c96ed2
13 changed files with 1469 additions and 0 deletions

View file

@ -0,0 +1,157 @@
import 'package:test/test.dart';
import '../lib/password_validator.dart';
// ============================================================
// PASSWORD VALIDATOR Workshop Starter
// Follow Red Green Refactor strictly.
// Uncomment one test at a time. Make it pass. Then the next.
// ============================================================
void main() {
late PasswordValidator validator;
setUp(() {
validator = PasswordValidator();
});
//
// STEP 1 Minimum length
// A password needs at least 8 characters.
// Start here. Everything else builds on a valid-length string.
//
group('Minimum length (8 characters)', () {
test('rejects a password shorter than 8 characters', () {
// TODO: uncomment when ready
// final result = validator.validate('Ab1!xyz');
// expect(result.isValid, isFalse);
// expect(result.errors, contains('Must be at least 8 characters long'));
});
test('accepts a password that is exactly 8 characters', () {
// TODO: uncomment when ready
// final result = validator.validate('Ab1!xyzw');
// expect(result.isValid, isTrue); // assuming other rules pass too
});
test('accepts a password longer than 8 characters', () {
// TODO: uncomment when ready
// final result = validator.validate('Ab1!xyzwqr');
// expect(result.isValid, isTrue);
});
});
//
// STEP 2 Uppercase letter
// At least one AZ character must be present.
//
group('Uppercase letter required', () {
test('rejects a password with no uppercase letters', () {
// TODO: uncomment when ready
// final result = validator.validate('ab1!xyzw');
// expect(result.isValid, isFalse);
// expect(result.errors, contains('Must contain at least one uppercase letter'));
});
test('accepts a password with at least one uppercase letter', () {
// TODO: uncomment when ready
// final result = validator.validate('Ab1!xyzw');
// expect(result.isValid, isTrue);
});
});
//
// STEP 3 Digit required
// At least one 09 character must be present.
//
group('Digit required', () {
test('rejects a password with no digits', () {
// TODO: uncomment when ready
// final result = validator.validate('Ab!!xyzw');
// expect(result.isValid, isFalse);
// expect(result.errors, contains('Must contain at least one digit'));
});
test('accepts a password with at least one digit', () {
// TODO: uncomment when ready
// final result = validator.validate('Ab1!xyzw');
// expect(result.isValid, isTrue);
});
});
//
// STEP 4 Special character required
// At least one of: ! @ # $ % ^ & *
//
group('Special character required', () {
test('rejects a password with no special characters', () {
// TODO: uncomment when ready
// final result = validator.validate('Ab1xxxyw');
// expect(result.isValid, isFalse);
// expect(result.errors, contains('Must contain at least one special character (!@#\$%^&*)'));
});
test('accepts a password with at least one special character', () {
// TODO: uncomment when ready
// final result = validator.validate('Ab1!xyzw');
// expect(result.isValid, isTrue);
});
});
//
// STEP 5 No spaces allowed
//
group('No spaces allowed', () {
test('rejects a password that contains a space', () {
// TODO: uncomment when ready
// final result = validator.validate('Ab1! xyz');
// expect(result.isValid, isFalse);
// expect(result.errors, contains('Must not contain spaces'));
});
test('accepts a password with no spaces', () {
// TODO: uncomment when ready
// final result = validator.validate('Ab1!xyzw');
// expect(result.isValid, isTrue);
});
});
//
// STEP 6 Multiple errors reported at once
// The validator collects ALL failures, not just the first.
// This is the refactor insight: rules should be independent.
//
group('Multiple errors reported together', () {
test('reports all failures for a completely invalid password', () {
// TODO: uncomment when ready
// final result = validator.validate('bad');
// expect(result.isValid, isFalse);
// expect(result.errors.length, equals(4)); // length, uppercase, digit, special char
});
test('a fully valid password has no errors', () {
// TODO: uncomment when ready
// final result = validator.validate('MyP@ssw0rd');
// expect(result.isValid, isTrue);
// expect(result.errors, isEmpty);
});
});
//
// BONUS Edge cases (if you finish early)
//
group('Edge cases', () {
test('empty string is invalid and reports the length error', () {
// TODO: uncomment when ready
// final result = validator.validate('');
// expect(result.isValid, isFalse);
// expect(result.errors, contains('Must be at least 8 characters long'));
});
test('exactly the minimum: 8 chars, all rules satisfied', () {
// TODO: uncomment when ready
// final result = validator.validate('Aa1!aaaa');
// expect(result.isValid, isTrue);
// expect(result.errors, isEmpty);
});
});
}