Laden...

Gitlab-Runner (selfhost) und MSTest - seltsames Verhalten - wo einen Fehler melden?

Erstellt von Taipi88 vor 4 Jahren Letzter Beitrag vor 4 Jahren 1.351 Views
Taipi88 Themenstarter:in
1.029 Beiträge seit 2010
vor 4 Jahren
Gitlab-Runner (selfhost) und MSTest - seltsames Verhalten - wo einen Fehler melden?

Hi,

vorab - für mehr Details - ich hatte bereits im Gitlab-Forum nachgefragt (siehe somit https://forum.gitlab.com/t/gitlab-runner-trying-to-open-and-listen-on-a-port-using-mstest-netcore-for-smtp/35585)

Grundlegend geht es um Folgendes:
Ich schreibe UnitTests die auch SMTP testen sollen - deshalb verwende ich quasi eine Minimalimplementierung eines SMTP-Servers (nDumbsterCore). Lokal - funkioniert das hervorragend - im Gitlab-Runner gibt's ein Timeout nach dem Entdecken der Tests ohne mir die Möglichkeit zu geben auch nur den geringsten Output zu erhalten.

Ursprünglich dachte ich, dass entweder das öffnen oder das verbinden zu einem so geöffneten Port Ärger mit sich bringt. Wie es scheint - ist das nicht der Fall. Denn das hinzufügen von "-t" zu dotnet test - genügt um den Test laufen zu lassen. Sogar parallelisiert.

Für mich steht fest: Das darf kein solches Problem beheben.

Die Fragen die sich mir nun stellen:
a) Warum behebt dieses Flag das Problem?
b) Stell ich mich selbst doof an?
c) Falls b) nicht zutrifft - wo kann ich ein entsprechendes Issue melden?
Gitlab oder eher Microsoft? (und wo genau?)

Für den Fall, dass jemand die Muße hat mein Problem nachzustellen:
a) Eine kurze C#-Klasse, welche genügt um im Gitlab-Runner ein Timeout auszulösen
Datei: MailTest.cs


using MailKit.Net.Smtp;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MimeKit;
using MimeKit.Text;
using nDumbsterCore.smtp;

namespace FluiTec.AppFx.Networking.Mail.Tests
{
    [TestClass]
    public class MailTest
    {
        [TestMethod]
        public void DoMailTest()
        {
            var server = SimpleSmtpServer.Start(25);

            using (var client = new SmtpClient())
            {
                client.ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true; // accepts all certs
                client.Connect("127.0.0.1", 25, false);
                client.AuthenticationMechanisms.Remove("XOAUTH2");
                client.Send(new MimeMessage
                {
                    From = { new MailboxAddress("Test", "test@example.com") },
                    To = { new MailboxAddress("Test", "test@example.com") },
                    Subject = "Subject",
                    Body = new TextPart(TextFormat.Plain)
                });
            }

            server.Stop();
        }
    }
}

b) Eine CI-Yaml-Datei um das Problem zu demonstrieren
Datei: .gitlab-ci.yml


image: mcr.microsoft.com/dotnet/core/sdk:3.1

stages:
    - build
    - test

variables:
    srcDir: "src"
    
before_script:
    - "cd $srcDir"
    - "dotnet restore"

build:
    stage: build
    script:
        - "dotnet build"
        
test:
    stage: test
    script:
        - "dotnet test" # funktioniert nicht (Timeout)
        - "dotnet test -t" # funktioniert (kein Timeout)

LG

16.806 Beiträge seit 2008
vor 4 Jahren

Zunächst Tipps:

  • Deaktivier Restore, wenn Du manuell einen Restore antriggerst
  • Deaktivier Auto-Build, wenn Du manuell den Build antriggerst

Das sind jeweils die --no-restore und --no-build Argumente.
Besonders bei .NET 2 hat das einige Issues gelöst

Beispiel (jedoch Azure DevOps, ich nutz kein GitLab):


- task: NuGetCommand@2
  displayName: "NuGet Restore"
  inputs:
    restoreSolution: '**/*.csproj'
    feedsToUse: config
    nugetConfigPath: NuGet.config

- task: DotNetCoreCLI@2
  displayName: ".NET build"
  inputs:
    projects: '**/*.csproj'
    arguments: --configuration ${{ parameters.buildConfiguration }} --no-restore

- task: DotNetCoreCLI@2
  displayName: ".NET test"
  inputs:
    command: test
    projects: 'tests/**/*.csproj'
    arguments: --no-restore --no-build

Denn das hinzufügen von "-t" zu dotnet test - genügt um den Test laufen zu lassen. Sogar parallelisiert.

Dein -t (Alias für --list-tests) Parameter führt dazu, dass keine Tests ausgeführt werden.
Das listet nur die Tests auf.

D.h., dass der Fehler selbst im Test sein dürfte; damit nicht Sache von GitLab ist.

Ich würde behaupten, dass client.Connect so nicht funktionieren wird.
Die Methode blockt, weil sie nicht verbinden kann. Das wird demnach 2 potentielle Resultate haben:

  • Du rennst in den Connection Timeout, gibt eine Exception
  • Du rennst in den Test Timeout, der Prozess wird abgeschossen

Vermutlich ist das Connection Timeout höher; damit greift das Test Timeout und Du bekommst Dein Fehler.

PS: das ist kein Unit Test, sondern ein Integrationstest.
Ein Unit Test hat keine externe Abhängigkeit.

Im Endeffekt hat der Integrationstest kein wirklichen Mehrwert. Die Aussagekraft ist nicht vorhanden.
Nur weil der Integrationstest gegen nDumbsterCore funktioniert, heisst das noch lange nicht, dass das gegen den produktiven SMTP funktioniert.

Taipi88 Themenstarter:in
1.029 Beiträge seit 2010
vor 4 Jahren

Hi,

vielen Dank für die Antwort und die Tipps.

Ok - dass damit keine Tests ausgeführt werden war mir nicht bewusst. Schade.

Nunja - dass der Fehler im Test liegt - ich führe diese ganzen Tests ja auch lokal aus - vollkommen ohne jedes Problem - und alle erfolgreich. (Dort führe ich das ganze per VS/ReShaper aus) Demnach sollte der Fehler doch eher in der Umgebung zu suchen sein. Korrekt? (Ist auch nicht so, als ob ich die Tests nur auf dem Server ausführe...)

Habe mal die Änderungen am CI vorgenommen und ein Timeout für den Client gesetzt.
Vom Grundprinzip hast du Recht - es ist ein Timeout, dass beim Connecten auftritt.
Aber wieso tritt das lokal nicht auf - aber auf diesem Docker-System?> Fehlermeldung:

X DoMailTest [185ms]
Error Message:
Test method FluiTec.AppFx.Networking.Mail.Tests.MailTest.DoMailTest threw exception:
System.IO.IOException: Connection timed out ---> System.Net.Sockets.SocketException: Connection timed out
Stack Trace:
at System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags)
at MailKit.Net.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 count)
--- End of inner exception stack trace ---
at MailKit.Net.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 count)
at MailKit.Net.Smtp.SmtpStream.ReadAheadAsync(Boolean doAsync, CancellationToken cancellationToken)
at MailKit.Net.Smtp.SmtpStream.ReadResponseAsync(Boolean doAsync, CancellationToken cancellationToken)
at MailKit.Net.Smtp.SmtpStream.ReadResponse(CancellationToken cancellationToken)
at MailKit.Net.Smtp.SmtpClient.DataAsync(FormatOptions options, MimeMessage message, Int64 size, Boolean doAsync, CancellationToken cancellationToken, ITransferProgress progress)
at MailKit.Net.Smtp.SmtpClient.SendAsync(FormatOptions options, MimeMessage message, MailboxAddress sender, IList`1 recipients, Boolean doAsync, CancellationToken cancellationToken, ITransferProgress progress)
at MailKit.Net.Smtp.SmtpClient.Send(FormatOptions options, MimeMessage message, CancellationToken cancellationToken, ITransferProgress progress)
at MailKit.MailTransport.Send(MimeMessage message, CancellationToken cancellationToken, ITransferProgress progress)
at FluiTec.AppFx.Networking.Mail.Tests.MailTest.DoMailTest() in /builds/fluitec/fluitec.appfx.networking/src/tests/FluiTec.AppFx.Networking.Mail.Tests/MailTest.cs:line 23

Am öffnen des Ports scheint es ja nicht zu hapern - warum also beim verbinden?

Was den Integrationstest angeht - ja das ist kein feiner UnitTest - grundlegend hatte ich aber hier die Optionen einen SmtpClient zu mocken (der in meinem Teil der API bislang gar nicht öffentlich zur Verfügung steht) und dort zu validieren - oder eben einen simplen SmtpService anzubieten, der eine Validierung erlaubt.

In anderen Worten: Du empfiehlst somit eher dem Client zu mocken?

LG und Vielen Dank

PS: Nunja - ein funktionierendes Verhalten gegenüber dem nDumbsterCore hat mir durchaus einen Mehrweg gebracht. Hab schnell gesehen, dass ich einige Paramter bei der Weitergabe getauscht hatte. Sicher - das ist kein echter SMTP-Server und soll es auch niemals werden, da ich gar keine Mails verschicken möchte in simplen Tests. Auf der anderen Seite verhält sich auch nicht jeder SMTP-Server wie der andere, womit ein solcher Test besser als nichts - aber auch sicher weniger gut als das Produktivsystem ist...

16.806 Beiträge seit 2008
vor 4 Jahren

Am öffnen des Ports scheint es ja nicht zu hapern - warum also beim verbinden?

Da ich keine Glaskugel hab, kann ich das nicht sagen.
Einfachste Antwort immer: da ist halt was nicht richtig konfiguriert.

In anderen Worten: Du empfiehlst somit eher dem Client zu mocken?

Willst Du ein Unit Test, dann musst Du den Client mocken, richtig.

D
152 Beiträge seit 2013
vor 4 Jahren

Woher weißt Du das es nicht am öffnen des Port liegt?

Versuch mal ein Port größer 1024, kleinere dürfen nur von root geöffnet werden.

Liste der standardisierten Ports

Taipi88 Themenstarter:in
1.029 Beiträge seit 2010
vor 4 Jahren

@Abt - ich leider auch nicht - ich hatte nur gehofft jemand hätte mal versucht auf dem Standard-MS-Image sich simpel mit einem Port zu verbinden.

@david.m
Hatte es bislang nicht erwähnt - aber mit einer der ersten Tests meinerseits war das simple Öffnen eines Ports, was problemlos durchging. (Auch ohne "-t"^^)
Zusätzlich hatte ich den UnitTest auch mal mit Port 50.000 ausgeführt.
Trotzdem Danke 😃

LG

PS:
Ich denke das weitere Verfolgen dieses Anliegens wird hier keinen so großen Zweck haben. Wenn keiner SMTP auf diese Art testet ist das wohl einfach nicht die Art, wie man das Problem angeht. (Wollte ja nur gründlich sein) Hab meine eigentlichen Klassen und UnitTests (diesmal wirklich) nun so umgeschrieben, dass alles was nicht gerade wirklich getestet wird per Moq gemockt wird. (Obwohl ich noch "etwas" an der Verifizierung eines IMailTransport.Send knabbere - das wird aber schon noch)