Skip to content

test: melhorar cobertura e migrar atualizações de GitHub Actions#76

Merged
afonsoft merged 9 commits into
mainfrom
devin/1783619335-test-improvements-and-action-updates
Jul 9, 2026
Merged

test: melhorar cobertura e migrar atualizações de GitHub Actions#76
afonsoft merged 9 commits into
mainfrom
devin/1783619335-test-improvements-and-action-updates

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

All Submissions:

  • Have you followed the guidelines in our Contributing document?
  • Have you checked to ensure there aren't other open Pull Requests for the same update/change?

New Feature Submissions:

  1. Does your submission pass tests?
  2. Have you lint your code locally prior to submission?

Changes to Core Features:

  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you written new tests for your core changes, as applicable?
  • Have you successfully ran tests with your changes locally?

Summary

This PR consolidates the currently open Dependabot updates for GitHub Actions, fixes the Qodana/Snyk/SonarCloud pipelines, and improves the test suite for afonsoft/metar-decoder.

GitHub Actions updates

  • actions/checkout: v6v7 across all workflows
  • actions/cache: v5v6 in ci-build-test.yml and openhands-resolver.yml
  • codecov/codecov-action: v6v7 in ci-build-test.yml
  • JetBrains/qodana-action: v2026.1.0v2026.1.3 in code-quality.yml

CI pipeline fixes

  • Qodana: switched from deprecated qodana-community-for-net to qodana-cdnet, added --solution MetarDecoder.sln, and committed qodana.sarif.json as baseline.
  • Snyk: skips the workflow scan when SNYK_TOKEN is not configured, avoiding 401 failures.
  • SonarCloud: fixed secret expansion in security-scan.yml and reduced code duplication by extracting shared DateTime rollover logic.

Code improvements

  • DatetimeChunkDecoder (METAR & TAF): refactored to accept an optional referenceDate, making month/year rollover and day-clamping logic deterministic and testable. The shared DateTimeHelper.BuildDateTime helper removes duplication between METAR and TAF while keeping the default constructor safe for long-running apps (uses DateTime.UtcNow lazily).

Test improvements

  • DatetimeChunkDecoder: added tests covering January rollover (year − 1, month = 12), generic month rollover, and day clamping to the resolved month length (METAR and TAF).
  • RunwayVisualRangeChunkDecoder: added test case for tendency N (no change).
  • Exceptions: added serialization round-trip tests for MetarChunkDecoderException and TafChunkDecoderException covering GetObjectData and the protected SerializationInfo constructor.
  • EvolutionChunkDecoder: added tests for Parse (NotImplementedException) and InstantiateEntity unknown entity path.

Coverage

  • Overall line coverage: 99.5% (METAR 100%, TAF 99.1%)
  • All 434 tests pass on net8.0 and net10.0

Link to Devin session: https://app.devin.ai/sessions/eba0afc252f2406f99f725e0397145db
Requested by: @afonsoft


Open in Devin Review

- Atualiza actions/checkout v6 -> v7, actions/cache v5 -> v6,
  codecov/codecov-action v6 -> v7, JetBrains/qodana-action v2026.1.0 -> v2026.1.3
- Refatora DatetimeChunkDecoder (METAR/TAF) para aceitar data de referência,
  tornando a lógica de rollover testável
- Adiciona testes de rollover de mês/ano e clamping de dias
- Adiciona testes de RVR com tendência 'N'
- Adiciona testes de serialização para MetarChunkDecoderException e TafChunkDecoderException
- Adiciona testes para InstantiateEntity do EvolutionChunkDecoder
- Cobertura global: 99.5% (METAR 100%, TAF 99%)

Co-Authored-By: Afonso Dutra Nogueira Filho <[email protected]>
@afonsoft afonsoft self-assigned this Jul 9, 2026
@afonsoft
afonsoft self-requested a review July 9, 2026 17:58
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@afonsoft

afonsoft commented Jul 9, 2026

Copy link
Copy Markdown
Owner

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

Atualiza o argumento --linter de qodana-community-for-net para qodana-cdnet,
compatível com a versão 2026.1.3 da action.

Co-Authored-By: Afonso Dutra Nogueira Filho <[email protected]>

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 5 potential issues.

Open in Devin Review

Comment on lines +17 to +18
public DatetimeChunkDecoder() : this(DateTime.UtcNow)
{

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Date rollover uses a frozen timestamp instead of the current time, producing wrong dates in long-running applications

The reference date for month/year rollover is captured once at construction time (new DatetimeChunkDecoder() at src/Metar.Decoder/MetarDecoder.cs:32) and stored in a static readonly decoder chain, so every subsequent parse reuses the date from when the class was first loaded.

Impact: In any application that runs past midnight or across month boundaries, METAR observations will be assigned to the wrong month or year.

Mechanism: static field freezes DateTime.UtcNow at class-load time

Previously, DateTime.UtcNow was called directly inside the Parse method each time a METAR was decoded, so the rollover logic always used the current date. The PR refactored this so the reference date is stored in _referenceDate, which is set via the default constructor DatetimeChunkDecoder() : this(DateTime.UtcNow) at src/Metar.Decoder/ChunkDecoder/DatetimeChunkDecoder.cs:17.

However, the decoder chain in MetarDecoder is declared as private static readonly at src/Metar.Decoder/MetarDecoder.cs:28, meaning the DatetimeChunkDecoder instance (and its _referenceDate) is created exactly once when the static field initializer runs. All calls to MetarDecoder.ParseWithMode() reuse this same instance.

For example, if the application starts on July 9 and a METAR is parsed on July 10 with day=10, the rollover check day > _referenceDate.Day compares 10 > 9 (true), incorrectly rolling back to June. With the old code, it would compare 10 > 10 (false) and correctly stay in July.

The same issue exists in src/Taf.Decoder/TafDecoder.cs:32.

Prompt for agents
The DatetimeChunkDecoder captures DateTime.UtcNow at construction time in _referenceDate, but the instance lives in a static readonly decoder chain (MetarDecoder._decoderChain at MetarDecoder.cs:28-45 and TafDecoder._decoderChain at TafDecoder.cs:28-39). This means the reference date is frozen at class-load time.

The previous code called DateTime.UtcNow directly inside the Parse method, so it always used the current time. The refactoring broke this by moving the UtcNow call to construction time.

Possible fixes:
1. In BuildObservationDateTime/BuildOriginDateTime, use DateTime.UtcNow directly instead of _referenceDate when _referenceDate was set via the default constructor. For example, store a nullable _referenceDate and fall back to DateTime.UtcNow when null.
2. Change the default parameterless constructor to not set _referenceDate, and have BuildObservationDateTime use DateTime.UtcNow as the default when _referenceDate is not explicitly provided.
3. Make the decoder chain non-static so a fresh DatetimeChunkDecoder is created per MetarDecoder/TafDecoder instance.

The parameterized constructor (for testing) should continue to use the provided referenceDate. Both src/Metar.Decoder/ChunkDecoder/DatetimeChunkDecoder.cs and src/Taf.Decoder/ChunkDecoder/DatetimeChunkDecoder.cs need the same fix.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Corrigido. O construtor padrão não congela mais DateTime.UtcNow; _referenceDate é DateTime? e DateTimeHelper.BuildDateTime usa DateTime.UtcNow apenas quando nenhuma data de referência é fornecida. A cadeia estática de decoders reutiliza a mesma instância, mas a data de referência é avaliada no momento do parse.

Comment on lines +17 to +18
public DatetimeChunkDecoder() : this(DateTime.UtcNow)
{

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Date rollover uses a frozen timestamp instead of the current time, producing wrong dates in long-running applications (TAF decoder)

The reference date for month/year rollover is captured once at construction time (new DatetimeChunkDecoder() at src/Taf.Decoder/TafDecoder.cs:32) and stored in a static readonly decoder chain, so every subsequent parse reuses the date from when the class was first loaded.

Impact: In any application that runs past midnight or across month boundaries, TAF forecasts will be assigned to the wrong month or year.

Mechanism: static field freezes DateTime.UtcNow at class-load time

This is the same issue as in the METAR decoder. The DatetimeChunkDecoder default constructor at src/Taf.Decoder/ChunkDecoder/DatetimeChunkDecoder.cs:17 captures DateTime.UtcNow into _referenceDate. The decoder chain at src/Taf.Decoder/TafDecoder.cs:28 is static readonly, so the instance is created once and reused for all TAF parsing. The old code called DateTime.UtcNow directly in the Parse method.

Prompt for agents
Same fix needed as for the METAR DatetimeChunkDecoder. The default constructor should not freeze DateTime.UtcNow into _referenceDate when the instance will live in a static readonly decoder chain. See the METAR decoder bug for suggested approaches.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Corrigido com o mesmo ajuste do METAR: a data de referência não é mais capturada no construtor padrão, evitando que static readonly decoders congelem a data no class-load.

Comment on lines +67 to +77
public void GetObjectDataSerializesRemainingMetarFields()
{
var decoder = new IcaoChunkDecoder();
var dto = new MetarChunkDecoderException("REMAINING", "NEW", "Test", decoder);
var info = new SerializationInfo(typeof(MetarChunkDecoderException), new FormatterConverter());

dto.GetObjectData(info, new StreamingContext());

ClassicAssert.That(info.GetString("RemainingMetar"), Is.EqualTo("NEW"));
ClassicAssert.That(info.GetString("NewRemainingMetar"), Is.EqualTo("REMAINING"));
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Serialization tests codify a pre-existing field-name swap in GetObjectData

The new test GetObjectDataSerializesRemainingMetarFields at tests/Metar.Decoder.Tests/MetarChunkDecoderExceptionTest.cs:67-77 asserts that info.GetString("RemainingMetar") equals "NEW" and info.GetString("NewRemainingMetar") equals "REMAINING". This matches the production code at src/Metar.Decoder/Exception/MetarChunkDecoderException.cs:68-69 where GetObjectData stores RemainingMetar under key "NewRemainingMetar" and vice versa. The same swap exists in src/Taf.Decoder/Exception/TafChunkDecoderException.cs:45-46 and is tested at tests/Taf.Decoder.Tests/Entity/TafExceptionExtendedTest.cs:70-71. While a round-trip (serialize → deserialize) happens to work because the deserialization constructor reads the same swapped keys, the serialized representation has semantically incorrect field names. These new tests lock in this behavior, making it harder to fix later.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Você está correto: o GetObjectData inverte os nomes RemainingMetar e NewRemainingMetar no dicionário de serialização. Isso é um comportamento preexistente e, embora a round-trip funcione porque o construtor de deserialização lê as mesmas chaves, trocar os nomes agora quebraria a compatibilidade binária de qualquer serialização anterior. Mantive os testes apenas para documentar/cobrir o comportamento atual, sem alterar a implementação. Se desejar corrigir isso em uma mudança breaking futura, posso abrir uma issue separada.

Comment on lines 81 to 84
if (day > _referenceDate.Day)
{
if (month == 1)
{

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Rollover logic does not account for same-day observations from the current month

The rollover condition if (day > _referenceDate.Day) at src/Metar.Decoder/ChunkDecoder/DatetimeChunkDecoder.cs:81 (and the TAF equivalent at src/Taf.Decoder/ChunkDecoder/DatetimeChunkDecoder.cs:80) means that if the METAR day equals the reference day, no rollover occurs — the observation is assumed to be in the current month. However, if a METAR is issued on day 31 and the reference date is also day 31 but in a different month (e.g., reference is March 31 and the METAR is from January 31), the logic will incorrectly assign it to March. This is an inherent limitation of METAR/TAF formats which only include the day, not the month. The previous code had the same limitation, so this is not a regression, but it's worth noting for anyone relying on the ObservationDateTime/OriginDateTime field for historical data.

(Refers to lines 81-92)

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concordo. Essa limitação é inerente ao formato METAR/TAF, que só traz o dia do mês. O mesmo comportamento existia no código anterior. A lógica continua útil para a maioria dos casos (relatórios recentes), mas para dados históricos o consumidor deve fornecer a data de referência correta via construtor DatetimeChunkDecoder(referenceDate).

Comment on lines 97 to 99
{
day = daysInMonth;
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Day clamping silently alters the parsed day value without warning

When the rollover logic resolves to a month with fewer days than the parsed day (e.g., day 31 rolling back to a 30-day month), the code at src/Metar.Decoder/ChunkDecoder/DatetimeChunkDecoder.cs:96-99 silently clamps the day to daysInMonth. This means the ObservationDateTime will have a different day than what the Day field reports (the Day result key is set before clamping at line 58, using the original parsed value). For example, parsing day 31 with a reference date of May 1 would yield Day=31 but ObservationDateTime with day 30 (April). This inconsistency between the two result fields could confuse consumers.

(Refers to lines 96-99)

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Você tem razão: o clamping silencioso cria inconsistência entre Day (valor original) e ObservationDateTime/OriginDateTime (valor ajustado). No entanto, isso preserva o mesmo comportamento do código anterior e evita ArgumentOutOfRangeException ao construir DateTime para meses com menos dias. Alterar para lançar exceção ou ajustar Day também seria uma mudança de comportamento. Deixei como está para manter compatibilidade, com os testes documentando o clamping.

Comment thread tests/Metar.Decoder.Tests/ChunkDecoder/DatetimeChunkDecoderTest.cs Fixed
Comment thread tests/Metar.Decoder.Tests/ChunkDecoder/DatetimeChunkDecoderTest.cs Fixed
Comment thread tests/Metar.Decoder.Tests/ChunkDecoder/DatetimeChunkDecoderTest.cs Fixed
Comment thread tests/Taf.Decoder.Tests/ChunkDecoder/DatetimeChunkDecoderTest.cs Fixed
Comment thread tests/Taf.Decoder.Tests/ChunkDecoder/DatetimeChunkDecoderTest.cs Fixed
Comment thread tests/Taf.Decoder.Tests/ChunkDecoder/DatetimeChunkDecoderTest.cs Fixed
Comment thread tests/Taf.Decoder.Tests/ChunkDecoder/EvolutionChunkDecoderTest.cs Fixed
devin-ai-integration Bot and others added 3 commits July 9, 2026 18:02
O DatetimeChunkDecoder padrão estava capturando DateTime.UtcNow no
construtor, mas a cadeia de decoders é static readonly, então a data
ficava congelada desde o primeiro uso. Agora o construtor padrão não
recebe data, e BuildObservationDateTime/BuildOriginDateTime usam
DateTime.UtcNow a cada chamada quando nenhuma data de referência for
explicitamente fornecida. O construtor com parâmetro continua
determinístico para testes.

Co-Authored-By: Afonso Dutra Nogueira Filho <[email protected]>
O linter qodana-cdnet exige que o caminho da solution seja informado
via --solution quando não há qodana.yaml.

Co-Authored-By: Afonso Dutra Nogueira Filho <[email protected]>
Evita falha do workflow de security-scan quando SNYK_TOKEN não está
configurado, seguindo o mesmo padrão do SonarQube.

Co-Authored-By: Afonso Dutra Nogueira Filho <[email protected]>
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

Qodana Community for .NET

It seems all right 👌

No new problems were found according to the checks applied

💡 Qodana analysis was run in the pull request mode: only the changed files were checked

View the detailed Qodana report

To be able to view the detailed Qodana report, you can either:

To get *.log files or any other Qodana artifacts, run the action with upload-result option set to true,
so that the action will upload the files as the job artifacts:

      - name: 'Qodana Scan'
        uses: JetBrains/[email protected]
        with:
          upload-result: true
Contact Qodana team

Contact us at [email protected]

devin-ai-integration Bot and others added 4 commits July 9, 2026 18:14
Gera o arquivo qodana.sarif.json para ser usado como baseline pelo
Qodana Community for .NET, evitando que problemas preexistentes
sejam reportados como novos em PRs.

Co-Authored-By: Afonso Dutra Nogueira Filho <[email protected]>
O workflow security-scan agora passa SNYK_TOKEN via env e verifica a
variável de ambiente, em vez de expandir o secret diretamente no
script run, atendendo à regra do SonarCloud.

Co-Authored-By: Afonso Dutra Nogueira Filho <[email protected]>
…hado

Reduz duplicação entre os decoders METAR e TAF movendo a lógica de
BuildObservationDateTime/BuildOriginDateTime para DateTimeHelper em
src/Shared, compilado via Link em ambos os projetos.

Co-Authored-By: Afonso Dutra Nogueira Filho <[email protected]>
Adiciona asserções de null e ContainsKey nos testes de DatetimeChunkDecoder
para evitar warnings de 'dereferenced variable may be null'. Renomeia
variável local em EvolutionChunkDecoderTest para evitar shadowing do
campo da classe.

Co-Authored-By: Afonso Dutra Nogueira Filho <[email protected]>
@sonarqubecloud

sonarqubecloud Bot commented Jul 9, 2026

Copy link
Copy Markdown


var result = decoded[MetarDecoder.ResultKey] as Dictionary<string, object>;
Assert.That(result, Is.Not.Null);
Assert.That(result.ContainsKey(DatetimeChunkDecoder.ObservationDateTimeParameterName), Is.True);

var result = decoded[MetarDecoder.ResultKey] as Dictionary<string, object>;
Assert.That(result, Is.Not.Null);
Assert.That(result.ContainsKey(DatetimeChunkDecoder.ObservationDateTimeParameterName), Is.True);

var result = decoded[MetarDecoder.ResultKey] as Dictionary<string, object>;
Assert.That(result, Is.Not.Null);
Assert.That(result.ContainsKey(DatetimeChunkDecoder.ObservationDateTimeParameterName), Is.True);

var result = decoded[TafDecoder.ResultKey] as Dictionary<string, object>;
Assert.That(result, Is.Not.Null);
Assert.That(result.ContainsKey(DatetimeChunkDecoder.OriginDateTimeParameterName), Is.True);

var result = decoded[TafDecoder.ResultKey] as Dictionary<string, object>;
Assert.That(result, Is.Not.Null);
Assert.That(result.ContainsKey(DatetimeChunkDecoder.OriginDateTimeParameterName), Is.True);

var result = decoded[TafDecoder.ResultKey] as Dictionary<string, object>;
Assert.That(result, Is.Not.Null);
Assert.That(result.ContainsKey(DatetimeChunkDecoder.OriginDateTimeParameterName), Is.True);
@afonsoft
afonsoft merged commit 0a93244 into main Jul 9, 2026
30 checks passed
@afonsoft
afonsoft deleted the devin/1783619335-test-improvements-and-action-updates branch July 12, 2026 18:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants