From 9951ce95606a4f438c87e7abb2e20d302e52e1dc Mon Sep 17 00:00:00 2001 From: Roman Cinis <52065414+tsinis@users.noreply.github.com> Date: Mon, 16 Feb 2026 19:49:57 +0100 Subject: [PATCH 1/2] chore: update copyright year to 2026 and adjust analysis options - Updated copyright year in LICENSE, num_status_code_extension.dart, status_code.dart, and status_code_extension.dart files. - Modified analysis_options.yaml to change rules and add new metrics. - Adjusted pubspec.yaml for dependency updates and environment SDK version. - Cleaned up ignore statements in various Dart files for better clarity. --- LICENSE | 2 +- analysis_options.yaml | 152 +++++++++++-------- example/lib/main.dart | 7 +- example/pubspec.yaml | 2 +- example/test/main_test.dart | 4 + lib/src/num_status_code_extension.dart | 4 +- lib/src/status_code.dart | 3 +- lib/src/status_code_extension.dart | 3 +- pubspec.yaml | 18 +-- test/src/num_status_code_extension_test.dart | 17 ++- test/src/status_code_extension_test.dart | 3 +- test/src/status_code_test.dart | 2 +- 12 files changed, 131 insertions(+), 86 deletions(-) diff --git a/LICENSE b/LICENSE index 02afef9..ef80594 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ BSD 3-Clause License -Copyright (c) 2025, Roman Cinis +Copyright (c) 2026, Roman Cinis Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/analysis_options.yaml b/analysis_options.yaml index cf67259..fd5b073 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -44,51 +44,52 @@ dart_code_metrics: metrics-exclude: - test/** - example/** - rules: + pubspec-rules: # Disabled: - - arguments-ordering: false # This will be a breaking change. - - avoid-adjacent-strings: false # It's not being used on one line strings. - - avoid-collection-mutating-methods: false # Mostly false positives. - - avoid-constant-conditions: false # A lot of false positives (e.g. outside of tests). - - avoid-continue: false # Just a matter of style. - - avoid-default-tostring: false # Doesn't make sense here. - - avoid-deprecated-usage: false # Using default deprecated_member_use_from_same_package. - - avoid-duplicate-collection-elements: false # They are being used on purpose. - - avoid-ignoring-return-values: false # Mostly, collection methods are ignored. - - avoid-inferrable-type-arguments: false # Against prefer-explicit-type-arguments. - - avoid-long-files: false # A lot of data and collections. - - avoid-nullable-interpolation: false # Doesn't work for toString() methods. - - avoid-unnecessary-nullable-fields: false # Fields could be nullable + has default values. - - avoid-unsafe-collection-methods: false # They are being used on purpose. - - enum-constants-ordering: false # This might be a breaking change. - - match-getter-setter-field-names: false # Most of those are just aliases. - - max-imports: false # Sadly not very suitable for the packages. - - no-empty-string: false # This will be a breaking change. - - no-equal-arguments: false # Not our use-case here. - - no-magic-number: false # Packages contain a lot of numbers. - - no-magic-string: false # Packages contain a lot of strings. - - parameters-ordering: false # This will be a breaking change. - - prefer-test-structure: false # Matter of style. - - prefer-boolean-prefixes: false # This will be a breaking change. - - prefer-correct-throws: false # No DCM annotations or Meta in Dart packages. - - prefer-explicit-parameter-names: false # This will be dev. defined. - - prefer-prefixed-global-constants: false # This will be a breaking change. - - prefer-test-matchers: false # Matter of style + introduces coupling in tests. + - add-resolution-workspace: false # This is not a workspace package. + - prefer-correct-package-name: false # Different naming conventions for Dart and Flutter packages. + - prefer-pinned-version-syntax: false # Conflicts with prefer-caret-version-syntax. + - prefer-publish-to-none: false # Those are not published packages. + - prefer-caret-version-syntax: false # Conflicts with avoid-any-version. + # Enabled: - - avoid-high-cyclomatic-complexity: - threshold: 40 - - avoid-long-functions: - exclude: - - "**/**_test.dart" + - avoid-any-version: true + - avoid-dependency-overrides: true + - dependencies-ordering: true + - newline-before-pubspec-entry: true + - prefer-correct-screenshots: true + - prefer-semver-version: true + - pubspec-ordering: + order: + - version + - name + - description + - publish_to + - maintainer + - repository + - issue_tracker + - homepage + - funding + - resolution + - topics + - screenshots + - environment + - dependencies + - dependency_overrides + - dev_dependencies + - prefer-commenting-pubspec-ignores: true + - prefer-correct-topics: true + + rules: + # With configs: - prefer-named-parameters: - max-number: 3 + max-number: 2 - avoid-nested-conditional-expressions: acceptable-level: 2 - prefer-correct-identifier-length: # i for index, x/y for math operations, sb for StringBuffer, io for IO, - # bc for BuildContext, a and b are convenient for sorting, e for the element, - # k for key, v for value. - exceptions: ["a", "b", "e", "i", "k", "v", "x", "y", "io", "bc", "sb"] + # a and b are convenient for sorting, e for the element, k for key, v for value. + exceptions: ["a", "b", "e", "i", "k", "v", "x", "y", "io", "sb"] - avoid-long-parameter-list: ignore-optional: true - prefer-typedefs-for-callbacks: @@ -96,12 +97,6 @@ dart_code_metrics: ignore-parameters: true ignore-type-arguments: false ignore-return-types: false - - avoid-passing-self-as-argument: - exclude: - - test/** - - avoid-top-level-members-in-tests: - exclude: - - lib/** - missing-test-assertion: include-assertions: - verify @@ -109,8 +104,6 @@ dart_code_metrics: - avoid-passing-async-when-sync-expected: exclude: - test/** - - avoid-returning-widgets: - allow-nullable: true - avoid-redundant-async: exclude: - test/** @@ -127,13 +120,32 @@ dart_code_metrics: - ^coverage.* - prefer-class-destructuring: min-occurrences: 4 - - prefer-single-declaration-per-file: - exclude: - - "**/test/**" + + # Disabled: + # Shorthands: + - prefer-returning-shorthands: false + - prefer-shorthands-with-constructors: false + - prefer-shorthands-with-enums: false + - prefer-shorthands-with-static-fields: false + # Other disabled: + - arguments-ordering: false # This will be a breaking change. + - avoid-continue: false # Just a matter of style. + - avoid-deprecated-usage: false # Using default deprecated_member_use_from_same_package. + - avoid-inferrable-type-arguments: false # Against prefer-explicit-type-arguments. + - avoid-long-files: false # A lot of data and collections. + - no-magic-number: false # Packages contain a lot of numbers. + - no-magic-string: false # Packages contain a lot of strings. + - parameters-ordering: false # This will be a breaking change. + - prefer-test-structure: false # Matter of style. + - prefer-boolean-prefixes: false # This will be a breaking change. + - prefer-correct-throws: false # No DCM annotations or Meta in Dart packages. + - prefer-test-matchers: false # Matter of style + introduces coupling in tests. # Enabled: - avoid-accessing-collections-by-constant-index: true - avoid-accessing-other-classes-private-members: true + - avoid-adjacent-strings: true + - avoid-always-null-parameters: true - avoid-assigning-to-static-field: true - avoid-assignments-as-conditions: true - avoid-async-call-in-sync-function: true @@ -147,16 +159,21 @@ dart_code_metrics: - avoid-collapsible-if: true - avoid-collection-equality-checks: true - avoid-collection-methods-with-unrelated-types: true + - avoid-collection-mutating-methods: true - avoid-commented-out-code: true - avoid-complex-arithmetic-expressions: true + - avoid-complex-conditions: true - avoid-complex-loop-conditions: true - avoid-conditions-with-boolean-literals: true - avoid-constant-assert-conditions: true + - avoid-constant-conditions: true - avoid-constant-switches: true - avoid-contradictory-expressions: true - avoid-declaring-call-method: true + - avoid-default-tostring: true - avoid-double-slash-imports: true - avoid-duplicate-cascades: true + - avoid-duplicate-collection-elements: true - avoid-duplicate-constant-values: true - avoid-duplicate-exports: true - avoid-duplicate-initializers: true @@ -180,8 +197,11 @@ dart_code_metrics: - avoid-future-tostring: true - avoid-generics-shadowing: true - avoid-global-state: true + - avoid-high-cyclomatic-complexity: true - avoid-identical-exception-handling-blocks: true - avoid-if-with-many-branches: true + - avoid-ignoring-return-values: true + - avoid-immediately-invoked-functions: true - avoid-implicitly-nullable-extension-types: true - avoid-importing-entrypoint-exports: true - avoid-incomplete-copy-with: true @@ -191,6 +211,7 @@ dart_code_metrics: - avoid-keywords-in-wildcard-pattern: true - avoid-late-final-reassignment: true - avoid-local-functions: true + - avoid-long-functions: true - avoid-long-records: true - avoid-map-keys-contains: true - avoid-missed-calls: true @@ -209,6 +230,7 @@ dart_code_metrics: - avoid-nested-extension-types: true - avoid-nested-futures: true - avoid-nested-records: true + - avoid-nested-shorthands: true - avoid-nested-streams-and-futures: true - avoid-nested-switch-expressions: true - avoid-nested-switches: true @@ -219,11 +241,13 @@ dart_code_metrics: - avoid-non-final-exception-class-fields: true - avoid-non-null-assertion: true - avoid-not-encodable-in-to-json: true + - avoid-nullable-interpolation: true - avoid-nullable-parameters-with-default-values: true - avoid-nullable-tostring: true - avoid-one-field-records: true - avoid-only-rethrow: true - avoid-passing-default-values: true + - avoid-passing-self-as-argument: true - avoid-positional-record-field-access: true - avoid-recursive-calls: true - avoid-recursive-tostring: true @@ -244,10 +268,13 @@ dart_code_metrics: - avoid-slow-collection-methods: true - avoid-stream-tostring: true - avoid-substring: true + - avoid-suspicious-global-reference: true - avoid-suspicious-super-overrides: true - avoid-throw-in-catch-block: true - avoid-throw-objects-without-tostring: true + - avoid-top-level-members-in-tests: true - avoid-type-casts: true + - avoid-unassigned-fields: true - avoid-unassigned-late-fields: true - avoid-unassigned-stream-subscriptions: true - avoid-uncaught-future-errors: true @@ -255,6 +282,7 @@ dart_code_metrics: - avoid-unknown-pragma: true - avoid-unnecessary-block: true - avoid-unnecessary-call: true + - avoid-unnecessary-collections: true - avoid-unnecessary-compare-to: true - avoid-unnecessary-conditionals: true - avoid-unnecessary-constructor: true @@ -272,6 +300,7 @@ dart_code_metrics: - avoid-unnecessary-local-variable: true - avoid-unnecessary-negations: true - avoid-unnecessary-null-aware-elements: true + - avoid-unnecessary-nullable-fields: true - avoid-unnecessary-nullable-parameters: true - avoid-unnecessary-nullable-return-type: true - avoid-unnecessary-overrides: true @@ -286,6 +315,7 @@ dart_code_metrics: - avoid-unrelated-type-assertions: true - avoid-unrelated-type-casts: true - avoid-unremovable-callbacks-in-listeners: true + - avoid-unsafe-collection-methods: true - avoid-unsafe-reduce: true - avoid-unused-after-null-check: true - avoid-unused-assignment: true @@ -299,21 +329,27 @@ dart_code_metrics: - binary-expression-operand-order: true - dispose-class-fields: true - double-literal-format: true + - enum-constants-ordering: true - function-always-returns-null: true - function-always-returns-same-value: true - handle-throwing-invocations: true - map-keys-ordering: true - match-base-class-default-value: true + - match-getter-setter-field-names: true - match-lib-folder-structure: true - match-positional-field-names-on-assignment: true + - max-imports: true - move-records-to-typedefs: true - move-variable-closer-to-its-usage: true - move-variable-outside-iteration: true - newline-before-case: true + - newline-before-constructor: true - newline-before-method: true - newline-before-return: true - no-boolean-literal-compare: true - no-empty-block: true + - no-empty-string: true + - no-equal-arguments: true - no-equal-conditions: true - no-equal-nested-conditions: true - no-equal-switch-case: true @@ -321,6 +357,7 @@ dart_code_metrics: - no-equal-then-else: true - no-object-declaration: true - pass-correct-accepted-type: true + - pass-optional-argument: true - pattern-fields-ordering: true - prefer-abstract-final-static-class: true - prefer-add-all: true @@ -352,6 +389,7 @@ dart_code_metrics: - prefer-enums-by-name: true - prefer-expect-later: true - prefer-explicit-function-type: true + - prefer-explicit-parameter-names: true - prefer-explicit-type-arguments: true - prefer-extracting-function-callbacks: true - prefer-first: true @@ -367,6 +405,7 @@ dart_code_metrics: - prefer-null-aware-spread: true - prefer-overriding-parent-equality: true - prefer-parentheses-with-if-null: true + - prefer-prefixed-global-constants: true - prefer-private-extension-type-field: true - prefer-public-exception-classes: true - prefer-pushing-conditional-expressions: true @@ -376,6 +415,7 @@ dart_code_metrics: - prefer-returning-conditional-expressions: true - prefer-simpler-boolean-expressions: true - prefer-simpler-patterns-null-check: true + - prefer-single-declaration-per-file: true - prefer-specific-cases-first: true - prefer-specifying-future-value-type: true - prefer-static-method: true @@ -405,7 +445,6 @@ linter: omit_local_variable_types: false # Conflicts with: prefer-type-over-var omit_obvious_local_variable_types: false # Conflicts with: omit_local_variable_types and prefer-type-over-var. omit_obvious_property_types: false # Conflicts with: type_annotate_public_apis. - prefer_final_parameters: false # Conflicts with: avoid_final_parameters. prefer_double_quotes: false # Conflicts with: prefer_single_quotes. specify_nonobvious_local_variable_types: false # Conflicts with: avoid-explicit-type-declaration. unnecessary_final: false # Conflicts with: prefer_final_locals. @@ -414,7 +453,6 @@ linter: # Enabled: always_declare_return_types: true always_put_required_named_parameters_first: true - always_require_non_null_named_parameters: true annotate_overrides: true annotate_redeclares: true avoid_annotating_with_dynamic: true @@ -433,7 +471,6 @@ linter: avoid_init_to_null: true avoid_js_rounded_ints: true avoid_multiple_declarations_per_line: true - avoid_null_checks_in_equality_operators: true avoid_positional_boolean_parameters: true avoid_print: true avoid_private_typedef_functions: true @@ -441,8 +478,6 @@ linter: avoid_relative_lib_imports: true avoid_renaming_method_parameters: true avoid_return_types_on_setters: true - avoid_returning_null: true - avoid_returning_null_for_future: true avoid_returning_null_for_void: true avoid_returning_this: true avoid_setters_without_getters: true @@ -479,7 +514,6 @@ linter: empty_catches: true empty_constructor_bodies: true empty_statements: true - enable_null_safety: true eol_at_end_of_file: true exhaustive_cases: true file_names: true @@ -516,7 +550,6 @@ linter: one_member_abstracts: true only_throw_errors: true overridden_fields: true - package_api_docs: true package_names: true package_prefixed_library_names: true parameter_assignments: true @@ -531,13 +564,12 @@ linter: prefer_const_literals_to_create_immutables: true prefer_constructors_over_static_methods: true prefer_contains: true - prefer_single_quotes: true - prefer_equal_for_default_values: true prefer_expression_function_bodies: true prefer_final_fields: true prefer_final_in_for_each: true prefer_final_locals: true prefer_for_elements_to_map_fromIterable: true + prefer_single_quotes: true prefer_foreach: true prefer_function_declarations_over_variables: true prefer_generic_function_type_aliases: true @@ -561,8 +593,10 @@ linter: provide_deprecation_message: true public_member_api_docs: true recursive_getters: true + remove_deprecations_in_breaking_versions: true require_trailing_commas: true secure_pubspec_urls: true + simplify_variable_pattern: true slash_for_doc_comments: true sort_constructors_first: true sort_pub_dependencies: true @@ -607,11 +641,9 @@ linter: unnecessary_underscores: true unreachable_from_main: true unrelated_type_equality_checks: true - unsafe_html: true unsafe_variance: true use_enums: true use_function_type_syntax_for_parameters: true - use_if_null_to_convert_nulls_to_bools: true use_is_even_rather_than_modulo: true use_late_for_private_fields_and_variables: true use_named_constants: true diff --git a/example/lib/main.dart b/example/lib/main.dart index 83f1480..30c95ba 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,5 +1,5 @@ -// ignore_for_file: avoid_print, avoid-unused-parameters, prefer-static-class, -// ignore_for_file: prefer-extracting-function-callbacks +// ignore_for_file: avoid-nullable-interpolation, avoid-unused-parameters +// ignore_for_file: avoid_print, prefer-extracting-function-callbacks import 'dart:convert' as convert; @@ -12,7 +12,7 @@ Future main([List args = const [], http.Client? client]) => : _realClient(args, client); /// Run with `dart run lib/main.dart --simple` command from the `example` folder. -// ignore: avoid-unnecessary-futures, just an example. +// ignore: avoid-unnecessary-futures, prefer-static-class, just an example. Future _simple(List args) async { /// Checks if status code is >=200 & <=299. print(105.isSuccess); // Prints false. @@ -66,6 +66,7 @@ Future _simple(List args) async { return StatusCode.tryParse(args.join()); } +// ignore: prefer-static-class, just an example. Future _realClient(List arguments, [http.Client? client]) async { /// This example uses the Google Books API to search for books about `http`: /// https://developers.google.com/books/docs/overview diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 4b6573d..ada3a3e 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: functional_status_codes: path: ../ - http: any # From Google. + http: any # ignore: avoid-any-version, it's exampe app. dev_dependencies: dart_code_metrics_presets: ^2.26.1 # DCM. diff --git a/example/test/main_test.dart b/example/test/main_test.dart index c29a283..7248b2c 100644 --- a/example/test/main_test.dart +++ b/example/test/main_test.dart @@ -1,3 +1,5 @@ +// ignore_for_file: avoid-unsafe-collection-methods + import 'package:functional_status_codes/functional_status_codes.dart'; import 'package:http/http.dart'; import 'package:http/testing.dart'; @@ -6,6 +8,7 @@ import 'package:test/test.dart'; // ignore: avoid_relative_lib_imports, to separate packages. import '../lib/main.dart' as example; +// ignore: avoid-long-functions, just an example. void main() { const simple = 'simple'; @@ -24,6 +27,7 @@ void main() { ); }); + // ignore: avoid-long-functions, just an example. group('http example', () { test( 'with real client', diff --git a/lib/src/num_status_code_extension.dart b/lib/src/num_status_code_extension.dart index e200e01..f338ef0 100644 --- a/lib/src/num_status_code_extension.dart +++ b/lib/src/num_status_code_extension.dart @@ -1,6 +1,7 @@ -// Copyright (c) 2025, Roman Cinis. All rights reserved. Use of this source code +// Copyright (c) 2026, Roman Cinis. All rights reserved. Use of this source code // is governed by a BSD-style license that can be found in the LICENSE file. +// ignore_for_file: prefer-typedefs-for-callbacks, avoid-long-parameter-list // ignore_for_file: avoid-shadowing, avoid-if-with-many-branches import 'status_code.dart'; @@ -9,6 +10,7 @@ import 'status_code.dart'; /// with HTTP status codes. extension NumStatusCodeExtension on T? { static const _outSideOfRangeMessage = + // ignore: avoid-adjacent-strings, it's not being used on one line strings. 'Value is outside of ' '''${StatusCode.continueHttp100}-${StatusCode.networkConnectTimeoutErrorHttp599}''' ' range'; diff --git a/lib/src/status_code.dart b/lib/src/status_code.dart index 5b24574..ef39655 100644 --- a/lib/src/status_code.dart +++ b/lib/src/status_code.dart @@ -1,4 +1,4 @@ -// Copyright (c) 2025, Roman Cinis. All rights reserved. Use of this source code +// Copyright (c) 2026, Roman Cinis. All rights reserved. Use of this source code // is governed by a BSD-style license that can be found in the LICENSE file. // ignore_for_file: format-comment, because of trailing dot at the end of URLs. @@ -1257,6 +1257,7 @@ extension type const StatusCode._(int _code) implements int { assert(from.isNotEmpty, 'The provided `from` iterable must not be empty'); final elementAt = (random ?? Random()).nextInt(from.length); + // ignore: avoid-unsafe-collection-methods, length is guaranteed to be > 0. return List.unmodifiable(from).elementAt(elementAt); } } diff --git a/lib/src/status_code_extension.dart b/lib/src/status_code_extension.dart index 3c344f3..1baba2b 100644 --- a/lib/src/status_code_extension.dart +++ b/lib/src/status_code_extension.dart @@ -1,7 +1,8 @@ -// Copyright (c) 2025, Roman Cinis. All rights reserved. Use of this source code +// Copyright (c) 2026, Roman Cinis. All rights reserved. Use of this source code // is governed by a BSD-style license that can be found in the LICENSE file. // ignore_for_file: avoid-long-parameter-list, avoid-high-cyclomatic-complexity +// ignore_for_file: prefer-typedefs-for-callbacks import 'status_code.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index cdea1ae..d697012 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,16 +11,16 @@ topics: - http - iana +screenshots: + - description: 'Good/Bad Comparison' + path: doc/comparison.webp + environment: - sdk: ^3.9.0 + sdk: ^3.11.0 dev_dependencies: coverage: ^1.15.0 # From Google. - dart_code_metrics_presets: ^2.26.1 # DCM. - lints: ^6.0.0 # From Google. - pana: ^0.23.0 # From Google. - test: ^1.26.3 # From Google. - -screenshots: - - description: 'Good/Bad Comparison' - path: doc/comparison.webp \ No newline at end of file + dart_code_metrics_presets: ^2.29.0 # DCM. + lints: ^6.1.0 # From Google. + pana: ^0.23.9 # From Google. + test: ^1.29.0 # From Google. diff --git a/test/src/num_status_code_extension_test.dart b/test/src/num_status_code_extension_test.dart index 9cdd95c..68609c3 100644 --- a/test/src/num_status_code_extension_test.dart +++ b/test/src/num_status_code_extension_test.dart @@ -1,4 +1,6 @@ +// ignore_for_file: avoid-unsafe-collection-methods, avoid-long-functions, // ignore_for_file: avoid-local-functions, avoid-misused-test-matchers +// ignore_for_file: avoid-nullable-interpolation, no-equal-arguments import 'package:functional_status_codes/src/num_status_code_extension.dart'; import 'package:functional_status_codes/src/status_code.dart'; @@ -287,12 +289,12 @@ void main() => group('NumStatusCodeExtension', () { group('maybeMapStatusCode', () { num maybeMapCode(num? number) => number.maybeMapStatusCode( + orElse: (_) => elseValue, isInformational: (value) => value, isSuccess: (value) => value, isRedirection: (value) => value, isClientError: (value) => value, isServerError: (value) => value, - orElse: (_) => elseValue, ); for (final number in globalWrongCases) { @@ -306,8 +308,8 @@ void main() => group('NumStatusCodeExtension', () { 'should return proper value for $testValue status code', () => expect( testValue.maybeMapStatusCode( - isStatusCode: (value) => value, orElse: (value) => value, + isStatusCode: (value) => value, ), testValue, ), @@ -334,12 +336,12 @@ void main() => group('NumStatusCodeExtension', () { group('maybeWhenStatusCode', () { int maybeWhenCode(num? number) => number.maybeWhenStatusCode( + orElse: () => elseValue, isInformational: () => StatusCode.continueHttp100, isSuccess: () => StatusCode.okHttp200, isRedirection: () => StatusCode.multipleChoicesHttp300, isClientError: () => StatusCode.badRequestHttp400, isServerError: () => StatusCode.internalServerErrorHttp500, - orElse: () => elseValue, ); for (final number in globalWrongCases) { @@ -360,8 +362,8 @@ void main() => group('NumStatusCodeExtension', () { 'should return proper value for $testValue status code', () => expect( testValue.maybeWhenStatusCode( - isStatusCode: () => testValue, orElse: () => null, + isStatusCode: () => testValue, ), testValue, ), @@ -486,6 +488,7 @@ void main() => group('NumStatusCodeExtension', () { test( 'should return proper value for $testValue status code', () => expect( + // ignore: avoid-passing-self-as-argument, it's just a test. testValue.whenConstStatusCodeOrNull(isStatusCode: testValue), testValue, ), @@ -559,12 +562,12 @@ void main() => group('NumStatusCodeExtension', () { group('maybeMapToRegisteredStatusCode', () { StatusCode? maybeMapToRegisteredCode(num? numb) => numb.maybeMapToRegisteredStatusCode( + orElse: (value, _) => value, isInformational: (value) => value, isSuccess: (value) => value, isRedirection: (value) => value, isClientError: (value) => value, isServerError: (value) => value, - orElse: (value, _) => value, ); test( @@ -619,8 +622,8 @@ void main() => group('NumStatusCodeExtension', () { 'should return proper value for $testValue status code', () => expect( testValue.maybeMapToRegisteredStatusCode( - isStatusCode: (value) => value, orElse: (value, _) => value, + isStatusCode: (value) => value, ), basicCodes.first, ), @@ -671,8 +674,8 @@ void main() => group('NumStatusCodeExtension', () { 'common test for out of range', () => expect( 1.mapToRegisteredStatusCodeOrNull( - orElse: (_) => const StatusCode.custom(290), isInformational: (_) => const StatusCode.custom(144), + orElse: (_) => const StatusCode.custom(290), ), const StatusCode.custom(290), ), diff --git a/test/src/status_code_extension_test.dart b/test/src/status_code_extension_test.dart index ddb0b21..d09700c 100644 --- a/test/src/status_code_extension_test.dart +++ b/test/src/status_code_extension_test.dart @@ -1,4 +1,5 @@ -// ignore_for_file: deprecated_member_use_from_same_package, it's TODO! +// ignore_for_file: deprecated_member_use_from_same_package,avoid-long-functions +// ignore_for_file: no-empty-string, no-equal-arguments import 'package:functional_status_codes/src/status_code.dart'; import 'package:functional_status_codes/src/status_code_extension.dart'; diff --git a/test/src/status_code_test.dart b/test/src/status_code_test.dart index 82fd364..c962fe0 100644 --- a/test/src/status_code_test.dart +++ b/test/src/status_code_test.dart @@ -1,4 +1,4 @@ -// ignore_for_file: avoid-misused-test-matchers +// ignore_for_file: avoid-misused-test-matchers, avoid-long-functions import 'dart:math'; import 'package:functional_status_codes/src/num_status_code_extension.dart'; From c4ad6bf9901cf0fbafcfaaec65e949c9f7e4b887 Mon Sep 17 00:00:00 2001 From: Roman Cinis <52065414+tsinis@users.noreply.github.com> Date: Sun, 1 Mar 2026 00:11:54 +0100 Subject: [PATCH 2/2] chore(ci): update workflows and tests for improved status code handling - Refactored analyze and test workflows to use updated pana command. - Enhanced test coverage for status code parsing and custom status codes. - Updated Dart SDK version in pubspec.yaml and example pubspec.yaml. - Adjusted CHANGELOG for new capabilities in v3.0.0. --- .github/workflows/analyze.yaml | 14 +-- .github/workflows/test.yaml | 9 +- .vscode/tasks.json | 2 +- CHANGELOG.md | 2 +- example/pubspec.yaml | 8 +- lib/functional_status_codes.dart | 2 +- lib/src/status_code.dart | 10 +- pubspec.yaml | 5 +- test/src/num_status_code_extension_test.dart | 117 +++++++++++++++++++ test/src/status_code_extension_test.dart | 34 ++++++ test/src/status_code_test.dart | 78 +++++++++++++ 11 files changed, 248 insertions(+), 33 deletions(-) diff --git a/.github/workflows/analyze.yaml b/.github/workflows/analyze.yaml index b74e530..038fa65 100644 --- a/.github/workflows/analyze.yaml +++ b/.github/workflows/analyze.yaml @@ -45,12 +45,13 @@ jobs: run: dart pub upgrade - name: ๐Ÿ•ต๏ธ Analyze code - uses: ValentinVignal/action-dart-analyze@v1.1 + uses: ValentinVignal/action-dart-analyze@v2.1 if: ${{ github.event_name == 'pull_request' }} with: - fail-on: "format" - token: ${{ secrets.GITHUB_TOKEN }} + fail-on: "note" + fail-on-format: true format: true + token: ${{ secrets.GITHUB_TOKEN }} - name: ๐Ÿœ๏ธ Publish - dry run if: ${{ github.event_name == 'pull_request' }} @@ -60,8 +61,5 @@ jobs: if: ${{ github.event_name == 'pull_request' }} run: | sudo apt-get install webp - PANA=$(dart run pana . --no-warning) - PANA_SCORE=$(echo $PANA | sed -n "s/.*Points: \([0-9]*\)\/\([0-9]*\)./\1\/\2/p") - echo "score: $PANA_SCORE" - IFS='/' read -ra SCORE_ARR <<< "$PANA_SCORE"; SCORE=${SCORE_ARR[0]} - if (( SCORE < 160 )); then echo "Minimum score of 160 was not met! Got $SCORE."; exit 1; fi + dart pub global activate pana + dart pub global run pana . --no-warning --exit-code-threshold 0 diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 811df07..3851f03 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -37,14 +37,7 @@ jobs: run: dart pub upgrade - name: ๐Ÿงช Run Tests - run: | - dart test --timeout=1ms --fail-fast --coverage=coverage - dart run coverage:format_coverage -l -i coverage -o coverage/lcov.info --report-on=lib - - - name: ๐Ÿ“Š Check Code Coverage - uses: VeryGoodOpenSource/very_good_coverage@v3.0.0 - with: - min_coverage: 99 + run: dart run coverage:test_with_coverage --fail-under=99 - name: ๐Ÿ’ฏ Upload Code Coverage uses: codecov/codecov-action@v5.5.2 diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 7769f75..f49d956 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -35,7 +35,7 @@ }, { "type": "shell", - "command": "PANA=$(dart run pana . --no-warning); PANA_SCORE=$(echo $PANA | sed -n \"s/.*Points: \\([0-9]*\\)\\/\\([0-9]*\\)./\\1\\/\\2/p\"); echo \"score: $PANA_SCORE\"; IFS='/' read -ra SCORE_ARR <<< \"$PANA_SCORE\"; SCORE=${SCORE_ARR[0]}; if (( SCORE < 160 )); then echo \"Minimum score of 160 was not met! Got $SCORE.\"; exit 1; fi", + "command": "dart pub global run pana . --no-warning --exit-code-threshold 0", "label": "PANA Score Check" }, { diff --git a/CHANGELOG.md b/CHANGELOG.md index 4afca54..857a0a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -170,7 +170,7 @@ class ApiClient { } ``` -#### New Features +#### New Capabilities in v3.0.0 - New `mapToRegisteredStatusCodeOrNull` method added! diff --git a/example/pubspec.yaml b/example/pubspec.yaml index ada3a3e..55f9a0f 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -3,7 +3,7 @@ description: A simple one and modified http package example with use of function publish_to: none environment: - sdk: ^3.9.0 + sdk: ^3.11.0 dependencies: functional_status_codes: @@ -11,6 +11,6 @@ dependencies: http: any # ignore: avoid-any-version, it's exampe app. dev_dependencies: - dart_code_metrics_presets: ^2.26.1 # DCM. - lints: ^6.0.0 # From Google. - test: ^1.26.3 # From Google. \ No newline at end of file + dart_code_metrics_presets: ^2.29.0 # DCM. + lints: ^6.1.0 # From Google. + test: ^1.30.0 # From Google. \ No newline at end of file diff --git a/lib/functional_status_codes.dart b/lib/functional_status_codes.dart index ef61018..453dfd6 100644 --- a/lib/functional_status_codes.dart +++ b/lib/functional_status_codes.dart @@ -1,4 +1,4 @@ -// Copyright (c) 2024, Roman Cinis. All rights reserved. Use of this source code +// Copyright (c) 2026, Roman Cinis. All rights reserved. Use of this source code // is governed by a BSD-style license that can be found in the LICENSE file. /// This package provides a functional programming style diff --git a/lib/src/status_code.dart b/lib/src/status_code.dart index ef39655..8e32548 100644 --- a/lib/src/status_code.dart +++ b/lib/src/status_code.dart @@ -669,15 +669,11 @@ extension type const StatusCode._(int _code) implements int { /// server. static const incompatibleProtocolVersionsHttp464 = StatusCode._(464); - /// Unofficial status code. Unauthorized: 561 + /// Unofficial status code. Unauthorized (AWS Elastic Load Balancing): 561 /// /// An error around authentication returned by a server registered with a load - /// balancer. You configured a listener rule to - - /// Unauthorized (AWS Elastic Load Balancing): 561 - /// - /// Used by AWS Elastic Load Balancing when authentication has failed or - /// credentials are missing. + /// balancer. You configured a listener rule to authenticate users, but the + /// authentication has failed or credentials are missing. static const unauthorizedHttp561 = StatusCode._(561); /// Unofficial status code. Network Connect Timeout Error: 599 diff --git a/pubspec.yaml b/pubspec.yaml index d697012..ee505e0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,11 +16,10 @@ screenshots: path: doc/comparison.webp environment: - sdk: ^3.11.0 + sdk: ^3.10.0 dev_dependencies: coverage: ^1.15.0 # From Google. dart_code_metrics_presets: ^2.29.0 # DCM. lints: ^6.1.0 # From Google. - pana: ^0.23.9 # From Google. - test: ^1.29.0 # From Google. + test: ^1.30.0 # From Google. diff --git a/test/src/num_status_code_extension_test.dart b/test/src/num_status_code_extension_test.dart index 68609c3..ef8034c 100644 --- a/test/src/num_status_code_extension_test.dart +++ b/test/src/num_status_code_extension_test.dart @@ -973,4 +973,121 @@ void main() => group('NumStatusCodeExtension', () { expect(520.isRetryable, isFalse); }); }); + + group('isStatusCodeWithinRange with inverted min/max', () { + test('should return false when min > max', () { + expect(200.isStatusCodeWithinRange(min: 400, max: 100), isFalse); + expect(300.isStatusCodeWithinRange(min: 500, max: 200), isFalse); + }); + + test('should return true when min equals max and value matches', () { + expect(200.isStatusCodeWithinRange(min: 200, max: 200), isTrue); + }); + + test('should return false when min equals max and value differs', () { + expect(201.isStatusCodeWithinRange(min: 200, max: 200), isFalse); + }); + }); + + group('float handling', () { + test('double with .0 fraction behaves like int', () { + expect(200.0.isStatusCode, isTrue); + expect(200.0.isSuccess, isTrue); + expect(200.0.toRegisteredStatusCode(), StatusCode.okHttp200); + }); + + test('double with fractional part truncates to int', () { + // 200.5 truncates to 200 via toInt(). + expect(200.5.isStatusCode, isTrue); + expect(200.5.isSuccess, isTrue); + expect(200.9.isStatusCode, isTrue); + }); + + test('double outside valid range', () { + expect(99.9.isStatusCode, isFalse); + expect(600.0.isStatusCode, isFalse); + }); + + test('null num is not a status code', () { + // ignore: avoid-explicit-type-declaration, it's a test. + const num? nullNum = null; + expect(nullNum.isStatusCode, isFalse); + expect(nullNum.isSuccess, isFalse); + expect(nullNum.isInformational, isFalse); + expect(nullNum.toRegisteredStatusCode(), isNull); + }); + }); + + group('isStatusCode callback masking in maybeMapStatusCode', () { + test('isStatusCode takes priority over category callbacks', () { + final result = 200.maybeMapStatusCode( + orElse: (_) => 'orElse', + isStatusCode: (_) => 'isStatusCode', + isSuccess: (_) => 'isSuccess', + ); + expect( + result, + 'isStatusCode', + reason: 'isStatusCode is checked first, so isSuccess is unreachable', + ); + }); + + test('category callback fires when isStatusCode is not provided', () { + final result = 200.maybeMapStatusCode( + orElse: (_) => 'orElse', + isSuccess: (_) => 'isSuccess', + ); + expect(result, 'isSuccess'); + }); + }); + + group('isStatusCode callback masking in maybeWhenStatusCode', () { + test('isStatusCode takes priority over category callbacks', () { + final result = 200.maybeWhenStatusCode( + orElse: () => 'orElse', + isStatusCode: () => 'isStatusCode', + isSuccess: () => 'isSuccess', + ); + expect(result, 'isStatusCode'); + }); + + test('category callback fires when isStatusCode is not provided', () { + final result = 200.maybeWhenStatusCode( + orElse: () => 'orElse', + isSuccess: () => 'isSuccess', + ); + expect(result, 'isSuccess'); + }); + }); + + group('isStatusCode callback masking in whenStatusCodeOrNull', () { + test('isStatusCode takes priority over category callbacks', () { + final result = 200.whenStatusCodeOrNull( + isStatusCode: () => 'isStatusCode', + isSuccess: () => 'isSuccess', + ); + expect(result, 'isStatusCode'); + }); + }); + + group('isStatusCode masking in whenConstStatusCodeOrNull', () { + test('isStatusCode takes priority over category callbacks', () { + final result = 200.whenConstStatusCodeOrNull( + isStatusCode: 'isStatusCode', + isSuccess: 'isSuccess', + ); + expect(result, 'isStatusCode'); + }); + }); + + group('isStatusCode masking in maybeMapToRegisteredStatusCode', () { + test('isStatusCode takes priority over category callbacks', () { + final result = 200.maybeMapToRegisteredStatusCode( + orElse: (_, _) => 'orElse', + isStatusCode: (_) => 'isStatusCode', + isSuccess: (_) => 'isSuccess', + ); + expect(result, 'isStatusCode'); + }); + }); }); diff --git a/test/src/status_code_extension_test.dart b/test/src/status_code_extension_test.dart index d09700c..3f827f9 100644 --- a/test/src/status_code_extension_test.dart +++ b/test/src/status_code_extension_test.dart @@ -1099,4 +1099,38 @@ void main() => group('StatusCodeExtension', () { } }); }); + + group('Custom status codes with map/maybeMap', () { + test('maybeMap returns orElse for custom status code', () { + final result = custom.maybeMap(orElse: () => 'orElse'); + expect(result, 'orElse'); + }); + + test('maybeWhen returns orElse for custom status code', () { + final result = custom.maybeWhen(orElse: () => 'orElse'); + expect(result, 'orElse'); + }); + + test('whenOrNull returns null for custom status code', () { + final result = custom.whenOrNull(okHttp200: () => 'ok'); + expect(result, isNull); + }); + + test('whenConstOrNull returns null for custom status code', () { + final result = custom.whenConstOrNull(okHttp200: 'ok'); + expect(result, isNull); + }); + + test('custom code has isCustom true', () { + expect(custom.isCustom, isTrue); + }); + + test('custom code has correct reason', () { + expect(custom.reason, contains('Custom status code')); + }); + + test('custom code name fallback', () { + expect(custom.name, contains('customStatusCode')); + }); + }); }); diff --git a/test/src/status_code_test.dart b/test/src/status_code_test.dart index c962fe0..9c07489 100644 --- a/test/src/status_code_test.dart +++ b/test/src/status_code_test.dart @@ -187,4 +187,82 @@ void main() => group('$StatusCode', () { ); }); }); + + group('tryParse edge cases', () { + test( + 'should parse code from string with leading/trailing whitespace', + () => expect(StatusCode.tryParse(' 200 '), StatusCode.okHttp200), + ); + + test( + 'should parse code from negative number string containing valid digits', + () => expect(StatusCode.tryParse('-200'), StatusCode.okHttp200), + ); + + test( + 'should return first match from multi-code string', + () => expect(StatusCode.tryParse('200 404'), StatusCode.okHttp200), + ); + + test( + 'should return null for two-digit number string', + () => expect(StatusCode.tryParse('99'), isNull), + ); + + test( + 'should return null for string with no digits', + () => expect(StatusCode.tryParse('abc'), isNull), + ); + + test( + 'should extract first three-digit match from longer number', + // '12003' matches '120' first โ€” not a registered code. + () => expect(StatusCode.tryParse('12003'), isNull), + ); + + test( + 'should return null for empty string', + // ignore: no-empty-string, it's an edge case we want to test. + () => expect(StatusCode.tryParse(''), isNull), + ); + + test( + 'should return null for "000"', + () => expect(StatusCode.tryParse('000'), isNull), + ); + }); + + group('custom constructor edge cases', () { + test('valid custom code in gap between standard codes', () { + const custom = StatusCode.custom(109); + expect(custom, equals(109)); + }); + + test('valid custom code at upper boundary area', () { + const custom = StatusCode.custom(597); + expect(custom, equals(597)); + }); + + test('asserts for code equal to lowest bound 103', () { + expect( + () => StatusCode.custom(StatusCode.earlyHintsHttp103), + throwsA(isA()), + ); + }); + + test('asserts for code equal to upper bound 599', () { + expect( + () => StatusCode.custom(StatusCode.networkConnectTimeoutErrorHttp599), + throwsA(isA()), + ); + }); + + test('asserts for code less than lower bound', () { + expect(() => StatusCode.custom(50), throwsA(isA())); + }); + + test('asserts for code greater than upper bound', () { + expect(() => StatusCode.custom(600), throwsA(isA())); + }); + }); });