From 9a5966fe97922e1a06ca89204d090785e99f45bc Mon Sep 17 00:00:00 2001 From: "Zamrony P. Juhara" Date: Thu, 17 Nov 2022 03:03:45 +0700 Subject: [PATCH] implement explode() and improve unit test --- src/Helpers/StringUtils.pas | 78 +++++++++++- src/Helpers/Tests/HelpersTest.pas | 23 ++++ src/Helpers/Tests/StringUtilsTest.pas | 120 ++++++++++++++++++ .../Validations/validators.aliases.inc | 4 +- .../validators.implementations.inc | 7 + .../AtLeastOneAlphaValidatorTest.pas | 13 +- .../AtLeastOneDigitValidatorTest.pas | 13 +- .../AtLeastOneLowerAlphaValidatorTest.pas | 13 +- .../AtLeastOneSymbolValidatorTest.pas | 13 +- .../AtLeastOneUpperAlphaValidatorTest.pas | 15 +-- .../Validators/MixedCapsValidatorTest.pas | 13 +- tests/testrunner.pas | 3 +- 12 files changed, 267 insertions(+), 48 deletions(-) create mode 100644 src/Helpers/Tests/HelpersTest.pas create mode 100644 src/Helpers/Tests/StringUtilsTest.pas diff --git a/src/Helpers/StringUtils.pas b/src/Helpers/StringUtils.pas index 252a4d35..2260cf26 100644 --- a/src/Helpers/StringUtils.pas +++ b/src/Helpers/StringUtils.pas @@ -46,11 +46,25 @@ interface *-----------------------------------------------*) function slug(const originalStr : string) : string; + (*!------------------------------------------------ + * explode string by delimiter as array of string + *----------------------------------------------- + * @param cDelimiter string to separate each + * @param sValues string to explode + * @param iCount max number string to return, 0 means find all string. + * @return array of string + *------------------------------------------------ + * @credit https://www.matthewhipkin.co.uk/codelib/cgi-mailform-using-freepascal/ + * @credit http://www.jasonwhite.co.uk/delphi-explode-function-like-php-explode/ + *-----------------------------------------------*) + function explode(const cDelimiter, sValue : string; iCount : integer = 0) : TStringArray; + implementation uses - regexpr; + regexpr, + strutils; (*!------------------------------------------------ * join array of string with a delimiter @@ -101,9 +115,69 @@ implementation function slug(const originalStr : string) : string; begin result := trim(originalStr); - if (result <> '') then + if (result = '') then begin + exit; + end; + + //bugfix for FPC 3.0.4 which does not support \W + {$IF FPC_FULLVERSION > 30004} result := ReplaceRegExpr('[\W\s]+', lowercase(result), '-', true); + {$ELSE} + result := ReplaceRegExpr( + '[~!@#$%^&*()_\-+={}\[\]|\\:;"''<>,.?/\s]+', + lowercase(result), + '-', + true + ); + {$ENDIF} + + // remove - from beginning if any + if pos('-', result) = 1 then + begin + result := copy(result, 2, length(result) - 1); + end; + + // remove - from end if any + if rpos('-', result) = length(result) then + begin + result := copy(result, 1, length(result) - 1); + end; + end; + + (*!------------------------------------------------ + * explode string by delimiter as array of string + *----------------------------------------------- + * @param cDelimiter string to separate each + * @param sValues string to explode + * @param iCount max number string to return, 0 means find all string. + * @return array of string + *------------------------------------------------ + * @credit https://www.matthewhipkin.co.uk/codelib/cgi-mailform-using-freepascal/ + * @credit http://www.jasonwhite.co.uk/delphi-explode-function-like-php-explode/ + *-----------------------------------------------*) + function explode(const cDelimiter, sValue : string; iCount : integer = 0) : TStringArray; + var + s : string; + i, p: integer; + begin + s := sValue; + i := 0; + result := nil; + while length(s) > 0 do + begin + inc(i); + SetLength(result, i); + p := pos(cDelimiter, s); + if (p > 0) and ((i < iCount) or (iCount=0)) then + begin + result[i-1] := copy(s, 0, p-1); + s := copy(s, p + length(cDelimiter), length(s)); + end else + begin + result[i-1] := s; + s := ''; + end; end; end; end. diff --git a/src/Helpers/Tests/HelpersTest.pas b/src/Helpers/Tests/HelpersTest.pas new file mode 100644 index 00000000..ba3b3f5e --- /dev/null +++ b/src/Helpers/Tests/HelpersTest.pas @@ -0,0 +1,23 @@ +{*! + * Fano Web Framework (https://fanoframework.github.io) + * + * @link https://github.com/fanoframework/fano + * @copyright Copyright (c) 2018 - 2022 Zamrony P. Juhara + * @license https://github.com/fanoframework/fano/blob/master/LICENSE (MIT) + *} + +unit HelpersTest; + +interface + +{$MODE OBJFPC} +{$H+} + +uses + + fpcunit, + testregistry, + StringUtilsTest; + +implementation +end. diff --git a/src/Helpers/Tests/StringUtilsTest.pas b/src/Helpers/Tests/StringUtilsTest.pas new file mode 100644 index 00000000..29e8a9f8 --- /dev/null +++ b/src/Helpers/Tests/StringUtilsTest.pas @@ -0,0 +1,120 @@ +{*! + * Fano Web Framework (https://fanoframework.github.io) + * + * @link https://github.com/fanoframework/fano + * @copyright Copyright (c) 2018 - 2022 Zamrony P. Juhara + * @license https://github.com/fanoframework/fano/blob/master/LICENSE (MIT) + *} + +unit StringUtilsTest; + +interface + +{$MODE OBJFPC} +{$H+} + +uses + + fpcunit, + testregistry, + SysUtils, + StringUtils; + +type + + (*!------------------------------------------------ + * string utilities test case + *-------------------------------------------------- + * @author Zamrony P. Juhara + *-------------------------------------------------*) + TStringUtilsTest = class(TTestCase) + protected + procedure Setup(); override; + procedure TearDown(); override; + published + procedure TestJoinMultipleShouldPass(); + procedure TestJoinSingleShouldPass(); + procedure TestJoinEmptyShouldPass(); + procedure TestExplodeEmptyShouldPass(); + procedure TestExplodeSingleShouldPass(); + procedure TestExplodeMultipleShouldPass(); + procedure TestSlugShouldReturnCorrectSlug(); + end; + +implementation + + + procedure TStringUtilsTest.Setup(); + begin + end; + + procedure TStringUtilsTest.TearDown(); + begin + end; + + procedure TStringUtilsTest.TestJoinMultipleShouldPass(); + var joinedStr : string; + begin + joinedStr := join('&', [ 'a=b', 'c=d', 'e=f']); + AssertEquals('a=b&c=d&e=f', joinedStr); + end; + + procedure TStringUtilsTest.TestJoinEmptyShouldPass(); + var joinedStr : string; + begin + joinedStr := join('&', []); + AssertEquals('', joinedStr); + end; + + procedure TStringUtilsTest.TestJoinSingleShouldPass(); + var joinedStr : string; + begin + joinedStr := join('&', [ 'a=b']); + AssertEquals('a=b', joinedStr); + end; + + procedure TStringUtilsTest.TestExplodeEmptyShouldPass(); + var explodedStr : TStringArray; + len : integer; + begin + explodedStr := explode('&', ''); + len := length(explodedStr); + AssertEquals(0, len); + end; + + procedure TStringUtilsTest.TestExplodeSingleShouldPass(); + var explodedStr : TStringArray; + len : integer; + begin + explodedStr := explode('&', 'a=b'); + len := length(explodedStr); + AssertEquals(len, 1); + AssertEquals('a=b', explodedStr[0]); + end; + + procedure TStringUtilsTest.TestExplodeMultipleShouldPass(); + var explodedStr : TStringArray; + len : integer; + begin + explodedStr := explode('&', 'a=b&c=d&e=f'); + len := length(explodedStr); + AssertEquals(3, len); + AssertEquals('a=b', explodedStr[0]); + AssertEquals('c=d', explodedStr[1]); + AssertEquals('e=f', explodedStr[2]); + end; + + procedure TStringUtilsTest.TestSlugShouldReturnCorrectSlug(); + begin + AssertEquals('', slug('')); + AssertEquals('', slug(' ')); + AssertEquals('test-hei-slug-to-slug-10', slug('test hei$#@slug to Slug 10')); + AssertEquals('test-hei-slug-to-slug-10', slug(' test hei$#@slug to Slug 10 ')); + AssertEquals('test-hei-slug-to-slug-10', slug('@#@!test hei$#@slug to Slug 10#@@')); + AssertEquals('test-hei-slug-to-slug-10', slug('@#@!test---hei$#@slug-to Slug 10#@@')); + AssertEquals('test-hei-slug-to-slug-10', slug('@#@!test--^-hei$#@slug-to Slug 10#@@')); + end; + +initialization + RegisterTest(TStringUtilsTest); +end. diff --git a/src/Includes/Security/Implementations/Validations/validators.aliases.inc b/src/Includes/Security/Implementations/Validations/validators.aliases.inc index 73b7b8ee..513251a5 100644 --- a/src/Includes/Security/Implementations/Validations/validators.aliases.inc +++ b/src/Includes/Security/Implementations/Validations/validators.aliases.inc @@ -137,6 +137,6 @@ TLongitudeValidator = LongitudeValidatorImpl.TLongitudeValidator; TAtLeastOneAlphaValidator = AtLeastOneAlphaValidatorImpl.TAtLeastOneAlphaValidator; TAtLeastOneLowerAlphaValidator = AtLeastOneLowerAlphaValidatorImpl.TAtLeastOneLowerAlphaValidator; TAtLeastOneUpperAlphaValidator = AtLeastOneUpperAlphaValidatorImpl.TAtLeastOneUpperAlphaValidator; -TAtLeastOneDigitValidator = AtLeastOneDigitValidatorImpl.TAtLeastDigitAlphaValidator; -TAtLeastOneSymbolValidator = AtLeastOneSymbolValidatorImpl.TAtLeastSymbolAlphaValidator; +TAtLeastOneDigitValidator = AtLeastOneDigitValidatorImpl.TAtLeastOneDigitValidator; +TAtLeastOneSymbolValidator = AtLeastOneSymbolValidatorImpl.TAtLeastOneSymbolValidator; TMixedCapsValidator = MixedCapsValidatorImpl.TMixedCapsValidator; diff --git a/src/Includes/Security/Implementations/Validations/validators.implementations.inc b/src/Includes/Security/Implementations/Validations/validators.implementations.inc index ead629c4..ce5dcbf6 100644 --- a/src/Includes/Security/Implementations/Validations/validators.implementations.inc +++ b/src/Includes/Security/Implementations/Validations/validators.implementations.inc @@ -124,3 +124,10 @@ EndWithValidatorImpl, LatitudeValidatorImpl, LongitudeValidatorImpl, + +AtLeastOneAlphaValidatorImpl, +AtLeastOneDigitValidatorImpl, +AtLeastOneLowerAlphaValidatorImpl, +AtLeastOneUpperAlphaValidatorImpl, +AtLeastOneSymbolValidatorImpl, +MixedCapsValidatorImpl, diff --git a/src/Security/Validation/Tests/Validators/AtLeastOneAlphaValidatorTest.pas b/src/Security/Validation/Tests/Validators/AtLeastOneAlphaValidatorTest.pas index e0fccb47..95afefa3 100644 --- a/src/Security/Validation/Tests/Validators/AtLeastOneAlphaValidatorTest.pas +++ b/src/Security/Validation/Tests/Validators/AtLeastOneAlphaValidatorTest.pas @@ -60,42 +60,42 @@ implementation var resValid : boolean; begin resValid := fValidator.isValid('my_key', fData, fRequest); - AssertEquals(resValid, true) + AssertEquals(true, resValid); end; procedure TAtLeastOneAlphaValidatorTest.TestInputContainsDigitsShouldFails(); var resValid : boolean; begin resValid := fValidator.isValid('my_digit', fData, fRequest); - AssertEquals(resValid, false) + AssertEquals(false, resValid); end; procedure TAtLeastOneAlphaValidatorTest.TestSymbolOnlyInputShouldFails(); var resValid : boolean; begin resValid := fValidator.isValid('my_symbol', fData, fRequest); - AssertEquals(resValid, false) + AssertEquals(false, resValid); end; procedure TAtLeastOneAlphaValidatorTest.TestSymbolWithAlphaInputShouldPass(); var resValid : boolean; begin resValid := fValidator.isValid('my_letter_symbol', fData, fRequest); - AssertEquals(resValid, true) + AssertEquals(true, resValid); end; procedure TAtLeastOneAlphaValidatorTest.TestOneAlphaInputShouldPass(); var resValid : boolean; begin resValid := fValidator.isValid('my_a', fData, fRequest); - AssertEquals(resValid, true) + AssertEquals(true, resValid); end; procedure TAtLeastOneAlphaValidatorTest.TestMixedAlphaCapsInputShouldPass(); var resValid : boolean; begin resValid := fValidator.isValid('my_abcd', fData, fRequest); - AssertEquals(resValid, true) + AssertEquals(true, resValid); end; initialization @@ -103,4 +103,3 @@ initialization RegisterTest(TAtLeastOneAlphaValidatorTest); end. - diff --git a/src/Security/Validation/Tests/Validators/AtLeastOneDigitValidatorTest.pas b/src/Security/Validation/Tests/Validators/AtLeastOneDigitValidatorTest.pas index 2c9aef9d..4e467f07 100644 --- a/src/Security/Validation/Tests/Validators/AtLeastOneDigitValidatorTest.pas +++ b/src/Security/Validation/Tests/Validators/AtLeastOneDigitValidatorTest.pas @@ -58,32 +58,32 @@ implementation procedure TAtLeastOneDigitValidatorTest.TestInputContainsAlphaShouldFails(); begin - AssertEquals(fValidator.isValid('my_key', fData, fRequest), false); + AssertEquals(false, fValidator.isValid('my_key', fData, fRequest)); end; procedure TAtLeastOneDigitValidatorTest.TestInputContainsDigitsShouldPass(); begin - AssertEquals(fValidator.isValid('my_digit', fData, fRequest), true); + AssertEquals(true, fValidator.isValid('my_digit', fData, fRequest)); end; procedure TAtLeastOneDigitValidatorTest.TestSymbolOnlyInputShouldFails(); begin - AssertEquals(fValidator.isValid('my_symbol', fData, fRequest), false); + AssertEquals(false, fValidator.isValid('my_symbol', fData, fRequest)); end; procedure TAtLeastOneDigitValidatorTest.TestSymbolWithAlphaInputShouldFails(); begin - AssertEquals(fValidator.isValid('my_letter_symbol', fData, fRequest), false); + AssertEquals(false, fValidator.isValid('my_letter_symbol', fData, fRequest)); end; procedure TAtLeastOneDigitValidatorTest.TestOneAlphaInputShouldFails(); begin - AssertEquals(fValidator.isValid('my_a', fData, fRequest), false); + AssertEquals(false, fValidator.isValid('my_a', fData, fRequest)); end; procedure TAtLeastOneDigitValidatorTest.TestMixedAlphaCapsInputShouldFails(); begin - AssertEquals(fValidator.isValid('my_abcd', fData, fRequest), false); + AssertEquals(false, fValidator.isValid('my_abcd', fData, fRequest)); end; initialization @@ -91,4 +91,3 @@ initialization RegisterTest(TAtLeastOneDigitValidatorTest); end. - diff --git a/src/Security/Validation/Tests/Validators/AtLeastOneLowerAlphaValidatorTest.pas b/src/Security/Validation/Tests/Validators/AtLeastOneLowerAlphaValidatorTest.pas index cc4a7b2c..a7f0ff70 100644 --- a/src/Security/Validation/Tests/Validators/AtLeastOneLowerAlphaValidatorTest.pas +++ b/src/Security/Validation/Tests/Validators/AtLeastOneLowerAlphaValidatorTest.pas @@ -60,42 +60,42 @@ implementation var resValid : boolean; begin resValid := fValidator.isValid('my_key', fData, fRequest); - AssertEquals(resValid, true) + AssertEquals(true, resValid); end; procedure TAtLeastOneLowerAlphaValidatorTest.TestInputContainsDigitsShouldFails(); var resValid : boolean; begin resValid := fValidator.isValid('my_digit', fData, fRequest); - AssertEquals(resValid, false) + AssertEquals(false, resValid); end; procedure TAtLeastOneLowerAlphaValidatorTest.TestSymbolOnlyInputShouldFails(); var resValid : boolean; begin resValid := fValidator.isValid('my_symbol', fData, fRequest); - AssertEquals(resValid, false) + AssertEquals(false, resValid); end; procedure TAtLeastOneLowerAlphaValidatorTest.TestSymbolWithAlphaInputShouldPass(); var resValid : boolean; begin resValid := fValidator.isValid('my_letter_symbol', fData, fRequest); - AssertEquals(resValid, true) + AssertEquals(true, resValid); end; procedure TAtLeastOneLowerAlphaValidatorTest.TestOneAlphaInputShouldPass(); var resValid : boolean; begin resValid := fValidator.isValid('my_a', fData, fRequest); - AssertEquals(resValid, true) + AssertEquals(true, resValid); end; procedure TAtLeastOneLowerAlphaValidatorTest.TestMixedAlphaCapsInputShouldPass(); var resValid : boolean; begin resValid := fValidator.isValid('my_abcd', fData, fRequest); - AssertEquals(resValid, true) + AssertEquals(true, resValid); end; initialization @@ -103,4 +103,3 @@ initialization RegisterTest(TAtLeastOneLowerAlphaValidatorTest); end. - diff --git a/src/Security/Validation/Tests/Validators/AtLeastOneSymbolValidatorTest.pas b/src/Security/Validation/Tests/Validators/AtLeastOneSymbolValidatorTest.pas index 800a9bb1..b3d3ea66 100644 --- a/src/Security/Validation/Tests/Validators/AtLeastOneSymbolValidatorTest.pas +++ b/src/Security/Validation/Tests/Validators/AtLeastOneSymbolValidatorTest.pas @@ -60,42 +60,42 @@ implementation var resValid : boolean; begin resValid := fValidator.isValid('my_key', fData, fRequest); - AssertEquals(resValid, false) + AssertEquals(false, resValid); end; procedure TAtLeastOneSymbolValidatorTest.TestInputContainsDigitsShouldFails(); var resValid : boolean; begin resValid := fValidator.isValid('my_digit', fData, fRequest); - AssertEquals(resValid, false) + AssertEquals(false, resValid); end; procedure TAtLeastOneSymbolValidatorTest.TestSymbolOnlyInputShouldPass(); var resValid : boolean; begin resValid := fValidator.isValid('my_symbol', fData, fRequest); - AssertEquals(resValid, true) + AssertEquals(true, resValid); end; procedure TAtLeastOneSymbolValidatorTest.TestSymbolWithAlphaInputShouldPass(); var resValid : boolean; begin resValid := fValidator.isValid('my_letter_symbol', fData, fRequest); - AssertEquals(resValid, true) + AssertEquals(true, resValid); end; procedure TAtLeastOneSymbolValidatorTest.TestOneAlphaInputShouldFails(); var resValid : boolean; begin resValid := fValidator.isValid('my_a', fData, fRequest); - AssertEquals(resValid, false) + AssertEquals(false, resValid); end; procedure TAtLeastOneSymbolValidatorTest.TestMixedAlphaCapsInputShouldFails(); var resValid : boolean; begin resValid := fValidator.isValid('my_abcd', fData, fRequest); - AssertEquals(resValid, false) + AssertEquals(false, resValid); end; initialization @@ -103,4 +103,3 @@ initialization RegisterTest(TAtLeastOneSymbolValidatorTest); end. - diff --git a/src/Security/Validation/Tests/Validators/AtLeastOneUpperAlphaValidatorTest.pas b/src/Security/Validation/Tests/Validators/AtLeastOneUpperAlphaValidatorTest.pas index c586e9b4..b9544617 100644 --- a/src/Security/Validation/Tests/Validators/AtLeastOneUpperAlphaValidatorTest.pas +++ b/src/Security/Validation/Tests/Validators/AtLeastOneUpperAlphaValidatorTest.pas @@ -61,49 +61,49 @@ implementation var resValid : boolean; begin resValid := fValidator.isValid('my_key', fData, fRequest); - AssertEquals(resValid, false) + AssertEquals(false, resValid); end; procedure TAtLeastOneUpperAlphaValidatorTest.TestInputContainsDigitsShouldFails(); var resValid : boolean; begin resValid := fValidator.isValid('my_digit', fData, fRequest); - AssertEquals(resValid, false) + AssertEquals(false, resValid); end; procedure TAtLeastOneUpperAlphaValidatorTest.TestSymbolOnlyInputShouldFails(); var resValid : boolean; begin resValid := fValidator.isValid('my_symbol', fData, fRequest); - AssertEquals(resValid, false) + AssertEquals(false, resValid); end; procedure TAtLeastOneUpperAlphaValidatorTest.TestSymbolWithLowerAlphaInputShouldFails(); var resValid : boolean; begin resValid := fValidator.isValid('my_letter_symbol', fData, fRequest); - AssertEquals(resValid, false) + AssertEquals(false, resValid); end; procedure TAtLeastOneUpperAlphaValidatorTest.TestSymbolWithUpperAlphaInputShouldPass(); var resValid : boolean; begin resValid := fValidator.isValid('my_upper_letter_symbol', fData, fRequest); - AssertEquals(resValid, true) + AssertEquals(true, resValid); end; procedure TAtLeastOneUpperAlphaValidatorTest.TestOneLowerAlphaInputShouldFails(); var resValid : boolean; begin resValid := fValidator.isValid('my_a', fData, fRequest); - AssertEquals(resValid, false) + AssertEquals(false, resValid); end; procedure TAtLeastOneUpperAlphaValidatorTest.TestMixedAlphaCapsInputShouldPass(); var resValid : boolean; begin resValid := fValidator.isValid('my_abcd', fData, fRequest); - AssertEquals(resValid, true) + AssertEquals(true, resValid); end; initialization @@ -111,4 +111,3 @@ initialization RegisterTest(TAtLeastOneUpperAlphaValidatorTest); end. - diff --git a/src/Security/Validation/Tests/Validators/MixedCapsValidatorTest.pas b/src/Security/Validation/Tests/Validators/MixedCapsValidatorTest.pas index 984e8d39..522fd1f7 100644 --- a/src/Security/Validation/Tests/Validators/MixedCapsValidatorTest.pas +++ b/src/Security/Validation/Tests/Validators/MixedCapsValidatorTest.pas @@ -59,32 +59,32 @@ implementation procedure TMixedCapsValidatorTest.TestInputContainsLowerAlphaShouldFails(); begin - AssertEquals(fValidator.isValid('my_key', fData, fRequest), false); + AssertEquals(false, fValidator.isValid('my_key', fData, fRequest)); end; procedure TMixedCapsValidatorTest.TestInputContainsDigitsShouldFails(); begin - AssertEquals(fValidator.isValid('my_digit', fData, fRequest), false); + AssertEquals(false, fValidator.isValid('my_digit', fData, fRequest)); end; procedure TMixedCapsValidatorTest.TestSymbolOnlyInputShouldFails(); begin - AssertEquals(fValidator.isValid('my_symbol', fData, fRequest), false); + AssertEquals(false, fValidator.isValid('my_symbol', fData, fRequest)); end; procedure TMixedCapsValidatorTest.TestSymbolWithAlphaInputShouldFails(); begin - AssertEquals(fValidator.isValid('my_letter_symbol', fData, fRequest), false); + AssertEquals(false, fValidator.isValid('my_letter_symbol', fData, fRequest)); end; procedure TMixedCapsValidatorTest.TestOneAlphaInputShouldFails(); begin - AssertEquals(fValidator.isValid('my_a', fData, fRequest), false); + AssertEquals(false, fValidator.isValid('my_a', fData, fRequest)); end; procedure TMixedCapsValidatorTest.TestMixedAlphaCapsInputShouldPass(); begin - AssertEquals(fValidator.isValid('my_abcd', fData, fRequest), true); + AssertEquals(true, fValidator.isValid('my_abcd', fData, fRequest)); end; initialization @@ -92,4 +92,3 @@ initialization RegisterTest(TMixedCapsValidatorTest); end. - diff --git a/tests/testrunner.pas b/tests/testrunner.pas index 8b21e77b..3cf0f9a2 100644 --- a/tests/testrunner.pas +++ b/tests/testrunner.pas @@ -16,7 +16,8 @@ classes, consoletestrunner, fano, - ValidatorsTest; + ValidatorsTest, + HelpersTest; type