diff --git a/go.mod b/go.mod index f36d1162e..3761c590e 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( github.com/mattermost/mattermost/server/v8 v8.0.0-20231111015533-48bf4e9bd879 github.com/mattn/godown v0.0.2-0.20211008172519-aa55d034e1f9 github.com/microsoft/kiota-abstractions-go v1.5.6 + github.com/microsoft/kiota-http-go v1.1.1 github.com/microsoftgraph/msgraph-sdk-go v1.32.0 github.com/microsoftgraph/msgraph-sdk-go-core v1.0.1 github.com/pborman/uuid v1.2.1 @@ -23,8 +24,9 @@ require ( github.com/prometheus/client_golang v1.18.0 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.8.4 - github.com/testcontainers/testcontainers-go v0.27.0 - github.com/testcontainers/testcontainers-go/modules/postgres v0.27.0 + github.com/testcontainers/testcontainers-go v0.28.0 + github.com/testcontainers/testcontainers-go/modules/mockserver v0.28.0 + github.com/testcontainers/testcontainers-go/modules/postgres v0.28.0 gitlab.com/golang-commonmark/markdown v0.0.0-20211110145824-bf3e522c626a golang.org/x/net v0.20.0 golang.org/x/oauth2 v0.16.0 @@ -42,17 +44,18 @@ require ( github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cjlapao/common-go v0.0.39 // indirect - github.com/containerd/containerd v1.7.11 // indirect + github.com/containerd/containerd v1.7.12 // indirect github.com/containerd/log v0.1.0 // indirect github.com/cpuguy83/dockercfg v0.3.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/disintegration/imaging v1.6.2 // indirect - github.com/docker/distribution v2.8.2+incompatible // indirect - github.com/docker/docker v24.0.7+incompatible // indirect - github.com/docker/go-connections v0.4.0 // indirect + github.com/distribution/reference v0.5.0 // indirect + github.com/docker/docker v25.0.2+incompatible // indirect + github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dyatlov/go-opengraph/opengraph v0.0.0-20220524092352-606d7b1e5f8a // indirect github.com/fatih/color v1.16.0 // indirect + github.com/felixge/httpsnoop v1.0.3 // indirect github.com/francoispqt/gojay v1.2.13 // indirect github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect github.com/go-logr/logr v1.4.1 // indirect @@ -82,7 +85,6 @@ require ( github.com/mattn/go-runewidth v0.0.15 // indirect github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/microsoft/kiota-authentication-azure-go v1.0.1 // indirect - github.com/microsoft/kiota-http-go v1.1.1 // indirect github.com/microsoft/kiota-serialization-form-go v1.0.0 // indirect github.com/microsoft/kiota-serialization-json-go v1.0.5 // indirect github.com/microsoft/kiota-serialization-multipart-go v1.0.0 // indirect @@ -90,13 +92,13 @@ require ( github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/moby/patternmatcher v0.6.0 // indirect github.com/moby/sys/sequential v0.5.0 // indirect + github.com/moby/sys/user v0.1.0 // indirect github.com/moby/term v0.5.0 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/oklog/run v1.1.0 // indirect github.com/oov/psd v0.0.0-20220121172623-5db5eafcecbb // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc5 // indirect - github.com/opencontainers/runc v1.1.5 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/philhofer/fwd v1.1.2 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect @@ -107,7 +109,7 @@ require ( github.com/prometheus/procfs v0.12.0 // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd // indirect - github.com/shirou/gopsutil/v3 v3.23.11 // indirect + github.com/shirou/gopsutil/v3 v3.23.12 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/std-uritemplate/std-uritemplate/go v0.0.50 // indirect github.com/stretchr/objx v0.5.1 // indirect @@ -123,6 +125,7 @@ require ( gitlab.com/golang-commonmark/linkify v0.0.0-20191026162114-a0c2df6c8f82 // indirect gitlab.com/golang-commonmark/mdurl v0.0.0-20191124015652-932350d1cb84 // indirect gitlab.com/golang-commonmark/puny v0.0.0-20191124015043-9f83538fa04f // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 // indirect go.opentelemetry.io/otel v1.22.0 // indirect go.opentelemetry.io/otel/metric v1.22.0 // indirect go.opentelemetry.io/otel/trace v1.22.0 // indirect diff --git a/go.sum b/go.sum index c5f877600..59ef7fb95 100644 --- a/go.sum +++ b/go.sum @@ -20,6 +20,7 @@ github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOEl github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 h1:DzHpqpoJVaCgOUdVHxE8QB52S6NiVdDQvGlny1qvPqA= github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= +github.com/BraspagDevelopers/mock-server-client v0.2.2 h1:Zro0OonNeaDwkkQGIxeJQfYweNKZ+m+8QIlDZAFRc/4= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM= github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= @@ -40,37 +41,30 @@ github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqy github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= -github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= github.com/cjlapao/common-go v0.0.39 h1:bAAUrj2B9v0kMzbAOhzjSmiyDy+rd56r2sy7oEiQLlA= github.com/cjlapao/common-go v0.0.39/go.mod h1:M3dzazLjTjEtZJbbxoA5ZDiGCiHmpwqW9l4UWaddwOA= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= -github.com/containerd/containerd v1.7.11 h1:lfGKw3eU35sjV0aG2eYZTiwFEY1pCzxdzicHP3SZILw= -github.com/containerd/containerd v1.7.11/go.mod h1:5UluHxHTX2rdvYuZ5OJTC5m/KJNs0Zs9wVoJm9zf5ZE= +github.com/containerd/containerd v1.7.12 h1:+KQsnv4VnzyxWcfO9mlxxELaoztsDEjOuCMPAuPqgU0= +github.com/containerd/containerd v1.7.12/go.mod h1:/5OMpE1p0ylxtEUGY8kuCYkDRzJm9NO1TFMWjUpdevk= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E= github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= -github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= +github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= +github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= -github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= -github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM= -github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/docker v25.0.2+incompatible h1:/OaKeauroa10K4Nqavw4zlhcDq/WBcPMc5DbjOGgozY= +github.com/docker/docker v25.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= +github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= @@ -81,10 +75,11 @@ github.com/enescakir/emoji v1.0.0/go.mod h1:Bt1EKuLnKDTYpLALApstIkAjdDrS/8IAgTkK github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= +github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= -github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= @@ -98,11 +93,10 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-resty/resty/v2 v2.10.0 h1:Qla4W/+TMmv0fOeeRqzEpXPLfTUnR5HZ1+lGs+CkiCo= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= @@ -120,7 +114,6 @@ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -147,13 +140,18 @@ github.com/gosimple/slug v1.13.1/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818J github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o= github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/grpc-gateway v1.5.0 h1:WcmKMm43DR7RdtlkEXQJyo5ws8iTp98CyhCCbOHMvNI= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= github.com/hashicorp/go-hclog v1.6.2 h1:NOtoftovWkDheyUM/8JW3QMiXyxJK3uHRK7wV04nD2I= github.com/hashicorp/go-hclog v1.6.2/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-plugin v1.6.0 h1:wgd4KxHJTVGGqWBq4QPB1i5BZNEx9BR8+OFmHDmTk8A= github.com/hashicorp/go-plugin v1.6.0/go.mod h1:lBS5MtSSBZk0SHc66KACcjjlU6WzEVP/8pwz68aMkCI= github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgx/v5 v5.5.3 h1:Ces6/M3wbDXYpM8JyyPD57ivTtJACFZJd885pdIaV2s= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= @@ -165,7 +163,6 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -238,16 +235,16 @@ github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJ github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= -github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= +github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= +github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= @@ -258,10 +255,6 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= -github.com/opencontainers/runc v1.1.5 h1:L44KXEpKmfWDcS02aeGm8QNTFXTo2D+8MYGDIJ/GDEs= -github.com/opencontainers/runc v1.1.5/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= -github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw= github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= @@ -297,15 +290,13 @@ github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd h1:CmH9+J6ZSsIjUK3dcGsnCnO41eRBOnY12zwkn5qVwgc= github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= -github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/shirou/gopsutil/v3 v3.23.11 h1:i3jP9NjCPUz7FiZKxlMnODZkdSIp2gnzfrvsu9CuWEQ= -github.com/shirou/gopsutil/v3 v3.23.11/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= +github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4= +github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= @@ -330,10 +321,8 @@ github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= @@ -354,23 +343,21 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= -github.com/testcontainers/testcontainers-go v0.27.0 h1:IeIrJN4twonTDuMuBNQdKZ+K97yd7VrmNGu+lDpYcDk= -github.com/testcontainers/testcontainers-go v0.27.0/go.mod h1:+HgYZcd17GshBUZv9b+jKFJ198heWPQq3KQIp2+N+7U= -github.com/testcontainers/testcontainers-go/modules/postgres v0.27.0 h1:gbA/HYjBIwOwhE/t4p3kIprfI0qsxCk+YVW7P9XFOus= -github.com/testcontainers/testcontainers-go/modules/postgres v0.27.0/go.mod h1:VFrFKUUgET2hNXStdtaC7uOIJWviFUrixhKeaVw/4F4= +github.com/testcontainers/testcontainers-go v0.28.0 h1:1HLm9qm+J5VikzFDYhOd+Zw12NtOl+8drH2E8nTY1r8= +github.com/testcontainers/testcontainers-go v0.28.0/go.mod h1:COlDpUXbwW3owtpMkEB1zo9gwb1CoKVKlyrVPejF4AU= +github.com/testcontainers/testcontainers-go/modules/mockserver v0.28.0 h1:Suti4kHmC1ow5APDBRboOOSTwyLaMWGu6icXCsCS30Y= +github.com/testcontainers/testcontainers-go/modules/mockserver v0.28.0/go.mod h1:9MVJEMBUFT6/baF4OL0X0xbKrmaiw6/fpE8ZydRP9NM= +github.com/testcontainers/testcontainers-go/modules/postgres v0.28.0 h1:ff0s4JdYIdNAVSi/SrpN2Pdt1f+IjIw3AKjbHau8Un4= +github.com/testcontainers/testcontainers-go/modules/postgres v0.28.0/go.mod h1:fXgcYpbyrduNdiz2qRZuYkmvqLnEqsjbQiBNYH1ystI= github.com/tinylib/msgp v1.1.9 h1:SHf3yoO2sGA0veCJeCBYLHuttAVFHGm2RHgNodW7wQU= github.com/tinylib/msgp v1.1.9/go.mod h1:BCXGB54lDD8qUEPmiG0cQQUANC4IUQyB2ItS2UDlO/k= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= -github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= @@ -397,12 +384,18 @@ gitlab.com/golang-commonmark/puny v0.0.0-20191124015043-9f83538fa04f/go.mod h1:T gitlab.com/opennota/wd v0.0.0-20180912061657-c5d65f63c638 h1:uPZaMiz6Sz0PZs3IZJWpU5qHKGNy///1pacZC9txiUI= gitlab.com/opennota/wd v0.0.0-20180912061657-c5d65f63c638/go.mod h1:EGRJaqe2eO9XGmFtQCvV3Lm9NLico3UhFwUpCG/+mVU= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 h1:x8Z78aZx8cOF0+Kkazoc7lwUNMGy0LrzEMxTm4BbTxg= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0/go.mod h1:62CPTSry9QZtOaSsE3tOzhx6LzDhHnXJ6xHeMNNiM6Q= go.opentelemetry.io/otel v1.22.0 h1:xS7Ku+7yTFvDfDraDIJVpw7XPyuHlB9MCiqqX5mcJ6Y= go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= go.opentelemetry.io/otel/metric v1.22.0 h1:lypMQnGyJYeuYPhOM/bgjbFM6WE44W1/T45er4d8Hhg= go.opentelemetry.io/otel/metric v1.22.0/go.mod h1:evJGjVpZv0mQ5QBRJoBF64yMuOf4xCWdXjK8pzFvliY= +go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= go.opentelemetry.io/otel/trace v1.22.0 h1:Hg6pPujv0XG9QaVbGOBVHunyuLcCC3jN7WEhPx83XD0= go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo= +go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -441,7 +434,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= @@ -471,23 +463,16 @@ golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -548,6 +533,8 @@ google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoA google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3 h1:1hfbdAfFbkmpg41000wDVqr7jUpK/Yo+LPnIxxGzmkg= +google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97 h1:W18sezcAYs+3tDZX4F80yctqa12jcP1PUS2gQu1zTPU= google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 h1:/jFB8jK5R3Sq3i/lmeZO0cATSzFfZaJq1J2Euan3XKU= google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0/go.mod h1:FUoWkonphQm3RhTS+kOEhF8h0iDpm4tdXolVCeZ9KKA= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= @@ -558,7 +545,6 @@ google.golang.org/grpc v1.60.0 h1:6FQAR0kM31P6MRdeluor2w2gPaS4SVNrD/DNTxrQ15k= google.golang.org/grpc v1.60.0/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/server/api.go b/server/api.go index 806c29417..1b962d5d8 100644 --- a/server/api.go +++ b/server/api.go @@ -75,8 +75,6 @@ func NewAPI(p *Plugin, store store.Store) *API { router.HandleFunc("/iframe/mattermostTab", api.iFrame).Methods("GET") router.HandleFunc("/iframe-manifest", api.iFrameManifest).Methods("GET") - api.registerClientMock() - return api } diff --git a/server/api_mock.go b/server/api_mock.go deleted file mode 100644 index d52c3ede1..000000000 --- a/server/api_mock.go +++ /dev/null @@ -1,181 +0,0 @@ -//go:build clientMock -// +build clientMock - -package main - -import ( - "encoding/json" - "net/http" - "reflect" - - "github.com/gorilla/mux" - "github.com/mattermost/mattermost-plugin-msteams/server/msteams/clientmodels" - "github.com/mattermost/mattermost-plugin-msteams/server/msteams/mocks" - "github.com/mattermost/mattermost-plugin-msteams/server/testutils/testmodels" - "github.com/mattermost/mattermost/server/public/model" - "github.com/pkg/errors" - "github.com/stretchr/testify/mock" - "golang.org/x/oauth2" -) - -var clientMock *mocks.Client - -func (a *API) registerClientMock() { - a.router.HandleFunc("/add-mock/{method:.*}", a.addMSTeamsClientMock).Methods(http.MethodPost) - a.router.HandleFunc("/reset-mocks", a.resetMSTeamsClientMocks).Methods(http.MethodPost) -} - -// resetMSTeamsClientMocks resets the msteams client mocks (for testing only) -func (a *API) resetMSTeamsClientMocks(w http.ResponseWriter, r *http.Request) { - userID := r.Header.Get("Mattermost-User-Id") - if userID == "" { - a.p.API.LogWarn("Not authorized") - http.Error(w, "not authorized", http.StatusUnauthorized) - return - } - - if !a.p.API.HasPermissionTo(userID, model.PermissionManageSystem) { - a.p.API.LogWarn("Insufficient permissions", "user_id", userID) - http.Error(w, "not able to authorize the user", http.StatusForbidden) - return - } - - clientMock = nil - newMock := getClientMock(a.p) - a.p.msteamsAppClient = newMock - w.WriteHeader(http.StatusOK) -} - -// addMSTeamsClientMock adds a new msteams client function mock (for testing only) -func (a *API) addMSTeamsClientMock(w http.ResponseWriter, r *http.Request) { - userID := r.Header.Get("Mattermost-User-Id") - if userID == "" { - a.p.API.LogWarn("Not authorized") - http.Error(w, "not authorized", http.StatusUnauthorized) - return - } - - if !a.p.API.HasPermissionTo(userID, model.PermissionManageSystem) { - a.p.API.LogWarn("Insufficient permissions", "user_id", userID) - http.Error(w, "not able to authorize the user", http.StatusForbidden) - return - } - - params := mux.Vars(r) - methodName := params["method"] - - mockClient := a.p.clientBuilderWithToken("", "", "", "", nil, nil).(*mocks.Client) - method, found := reflect.TypeOf(mockClient).MethodByName(methodName) - if !found { - a.p.API.LogError("Unable to mock the method, method not found", "MethodName", methodName) - http.Error(w, "method not found", http.StatusNotFound) - return - } - - paramsCount := method.Type.NumIn() - parameters := []interface{}{} - for x := 0; x < paramsCount; x++ { - parameters = append(parameters, mock.Anything) - } - - var mockCall testmodels.MockCallReturns - err := json.NewDecoder(r.Body).Decode(&mockCall) - if err != nil { - http.Error(w, "unable to mock the method", http.StatusBadRequest) - return - } - defer r.Body.Close() - var returnErr error - if mockCall.Err != "" { - returnErr = errors.New(mockCall.Err) - } - - var output any - - data, err := json.Marshal(mockCall.Returns) - if err != nil { - http.Error(w, "unable to mock the method", http.StatusBadRequest) - return - } - - switch mockCall.ReturnType { - case "": - output = nil - case "Chat": - output = &clientmodels.Chat{} - err = json.Unmarshal(data, &output) - case "ChatMember": - output = &clientmodels.ChatMember{} - err = json.Unmarshal(data, &output) - case "Attachment": - output = &clientmodels.Attachment{} - err = json.Unmarshal(data, &output) - case "Reaction": - output = &clientmodels.Reaction{} - err = json.Unmarshal(data, &output) - case "Mention": - output = &clientmodels.Mention{} - err = json.Unmarshal(data, &output) - case "Message": - output = &clientmodels.Message{} - err = json.Unmarshal(data, &output) - case "Subscription": - output = &clientmodels.Subscription{} - err = json.Unmarshal(data, &output) - case "Channel": - output = &clientmodels.Channel{} - err = json.Unmarshal(data, &output) - case "User": - output = &clientmodels.User{} - err = json.Unmarshal(data, &output) - case "Team": - output = &clientmodels.Team{} - err = json.Unmarshal(data, &output) - case "ActivityIds": - output = &clientmodels.ActivityIds{} - err = json.Unmarshal(data, &output) - } - if err != nil { - http.Error(w, "unable to mock the method", http.StatusBadRequest) - return - } - - returns := method.Type.NumOut() - switch returns { - case 0: - a.p.API.LogDebug("mocking", "method", methodName) - mockClient.On(methodName, parameters...) - case 1: - a.p.API.LogDebug("mocking", "method", methodName, "output", output) - mockClient.On(methodName, parameters...).Return(output) - case 2: - a.p.API.LogDebug("mocking", "method", methodName, "output", output, "returnErr", returnErr) - mockClient.On(methodName, parameters...).Return(output, returnErr) - case 3: - output1 := int64(mockCall.Returns.([]interface{})[0].(int)) - output2 := mockCall.Returns.([]interface{})[0].(string) - a.p.API.LogDebug("mocking", "method", methodName, "output1", output1, "output2", output2, "returnErr", returnErr) - mockClient.On(methodName, parameters...).Return(output1, output2, returnErr) - } - - w.WriteHeader(http.StatusOK) -} - -func getClientMock(p *Plugin) *mocks.Client { - p.API.LogInfo("Using mock client") - - if clientMock != nil { - return clientMock - } - newMock := mocks.Client{} - newMock.On("ClearSubscriptions").Return(nil) - newMock.On("RefreshToken", mock.Anything).Return(&oauth2.Token{}, nil) - newMock.On("RefreshSubscriptionsPeriodically", mock.Anything, mock.Anything).Return(nil) - newMock.On("SubscribeToChannels", mock.Anything, p.getConfiguration().WebhookSecret, "").Return("channel-subscription-id", nil) - newMock.On("SubscribeToChats", mock.Anything, p.getConfiguration().WebhookSecret, true, "").Return(&clientmodels.Subscription{ID: "chats-subscription-id"}, nil) - newMock.On("SubscribeToChannel", mock.Anything, mock.Anything, mock.Anything, p.getConfiguration().WebhookSecret, "").Return(&clientmodels.Subscription{ID: "channel-subscription-id"}, nil) - newMock.On("ListSubscriptions").Return([]*clientmodels.Subscription{}, nil) - newMock.On("GetAppCredentials", mock.Anything).Return([]clientmodels.Credential{}, nil) - clientMock = &newMock - return clientMock -} diff --git a/server/api_nomock.go b/server/api_nomock.go deleted file mode 100644 index 5dba91b4f..000000000 --- a/server/api_nomock.go +++ /dev/null @@ -1,13 +0,0 @@ -//go:build !clientMock -// +build !clientMock - -package main - -import "github.com/mattermost/mattermost-plugin-msteams/server/msteams/mocks" - -func getClientMock(_ *Plugin) *mocks.Client { - return nil -} - -func (a *API) registerClientMock() { -} diff --git a/server/ce2e/iframe_test.go b/server/ce2e/iframe_test.go index 1dca09bd6..5fd1492e6 100644 --- a/server/ce2e/iframe_test.go +++ b/server/ce2e/iframe_test.go @@ -18,7 +18,7 @@ import ( func TestIFrame(t *testing.T) { t.Parallel() - mattermost, _, tearDown := containere2e.NewE2ETestPlugin(t) + mattermost, _, _, tearDown := containere2e.NewE2ETestPlugin(t) defer tearDown() client, err := mattermost.GetAdminClient(context.Background()) require.NoError(t, err) diff --git a/server/ce2e/license_test.go b/server/ce2e/license_test.go index cf9ca4cf0..a006dcf22 100644 --- a/server/ce2e/license_test.go +++ b/server/ce2e/license_test.go @@ -11,7 +11,7 @@ import ( func TestRequiresLicense(t *testing.T) { t.Parallel() - mattermost, _, tearDown := containere2e.NewE2ETestPlugin(t, containere2e.WithoutLicense()) + mattermost, _, _, tearDown := containere2e.NewE2ETestPlugin(t, containere2e.WithoutLicense()) defer tearDown() client, err := mattermost.GetAdminClient(context.Background()) diff --git a/server/ce2e/plugin_test.go b/server/ce2e/plugin_test.go index 8b3b47e6c..9d8cbf6cf 100644 --- a/server/ce2e/plugin_test.go +++ b/server/ce2e/plugin_test.go @@ -2,11 +2,11 @@ package ce2e import ( "context" + "net/http" "strings" "testing" "time" - "github.com/mattermost/mattermost-plugin-msteams/server/msteams/clientmodels" "github.com/mattermost/mattermost-plugin-msteams/server/store/storemodels" "github.com/mattermost/mattermost-plugin-msteams/server/testutils/containere2e" "github.com/mattermost/mattermost/server/public/model" @@ -14,9 +14,12 @@ import ( "golang.org/x/oauth2" ) +var fakeToken = oauth2.Token{Expiry: time.Now().Add(1 * time.Hour), AccessToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjozMDE2MjM5MDIyfQ.Kilb7fc4QwqfCad501vbAc861Ik1-30ytRtk8ZxEpgM"} + func TestMessageHasBeenPostedNewMessageE2E(t *testing.T) { t.Parallel() - mattermost, store, tearDown := containere2e.NewE2ETestPlugin(t) + + mattermost, store, mockClient, tearDown := containere2e.NewE2ETestPlugin(t) defer tearDown() client, err := mattermost.GetAdminClient(context.Background()) @@ -39,7 +42,7 @@ func TestMessageHasBeenPostedNewMessageE2E(t *testing.T) { Message: "message", } - err = store.SetUserInfo(user.Id, "ms-user-id", &oauth2.Token{}) + err = store.SetUserInfo(user.Id, "ms-user-id", &fakeToken) require.NoError(t, err) t.Run("Without Channel Link", func(t *testing.T) { @@ -54,13 +57,54 @@ func TestMessageHasBeenPostedNewMessageE2E(t *testing.T) { }) t.Run("Everything OK", func(t *testing.T) { - containere2e.ResetMSTeamsClientMock(t, client) - containere2e.MockMSTeamsClient(t, client, "GetChannelInTeam", "Channel", clientmodels.Channel{ID: "ms-channel-id"}, "") - containere2e.MockMSTeamsClient(t, client, "SendMessageWithAttachments", "Message", clientmodels.Message{ID: "ms-post-id", LastUpdateAt: time.Now()}, "") + if err = mockClient.Reset(); err != nil { + t.Log(err) + } + + err = mockClient.Get("/v1.0/teams/ms-team-id/channels/ms-channel-id", map[string]any{ + "id": "ms-channel-id", + "createdDateTime": time.Now().Format(time.RFC3339), + "displayName": "test channel", + "description": "Test channel", + "membershipType": "standard", + }) + require.NoError(t, err) - _, _, err = client.ExecuteCommand(context.Background(), channel.Id, "/msteams link ms-team-id ms-channel-id") + newPostID := model.NewId() + err = mockClient.Post("/v1.0/teams/ms-team-id/channels/ms-channel-id/messages", map[string]any{ + "id": newPostID, + "etag": "1616990032035", + "messageType": "message", + "createdDateTime": time.Now().Format(time.RFC3339), + "lastModifiedDateTime": time.Now().Format(time.RFC3339), + "importance": "normal", + "locale": "en-us", + "webUrl": "https://teams.microsoft.com/l/message/ms-channel-id/test-message-id", + "from": map[string]any{ + "user": map[string]any{ + "@odata.type": "#microsoft.graph.teamworkUserIdentity", + "id": "ms-user-id", + "displayName": user.Username, + "userIdentityType": "aadUser", + "tenantId": "tenant-id", + }, + }, + "body": map[string]any{ + "contentType": "text", + "content": "Hello World", + }, + "channelIdentity": map[string]any{ + "teamId": "ms-team-id", + "channelId": "ms-channel-id", + }, + }) require.NoError(t, err) + require.Eventually(t, func() bool { + _, _, err = client.ExecuteCommand(context.Background(), channel.Id, "/msteams link ms-team-id ms-channel-id") + return err == nil + }, 5*time.Second, 500*time.Millisecond) + var newPost *model.Post newPost, _, err = client.CreatePost(context.Background(), &post) require.NoError(t, err) @@ -71,7 +115,7 @@ func TestMessageHasBeenPostedNewMessageE2E(t *testing.T) { if err != nil { return false } - if postInfo.MSTeamsID == "ms-post-id" { + if postInfo.MSTeamsID == newPostID { return true } return false @@ -79,11 +123,177 @@ func TestMessageHasBeenPostedNewMessageE2E(t *testing.T) { }) t.Run("Failing to deliver message to MSTeams", func(t *testing.T) { - containere2e.ResetMSTeamsClientMock(t, client) - containere2e.MockMSTeamsClient(t, client, "GetChannelInTeam", "Channel", clientmodels.Channel{ID: "ms-channel-id"}, "") - containere2e.MockMSTeamsClient(t, client, "SendMessageWithAttachments", "Message", nil, "Unable to send the message") + if err = mockClient.Reset(); err != nil { + t.Log(err) + } + + err = mockClient.Get("/v1.0/teams/ms-team-id/channels/ms-channel-id", map[string]any{ + "id": "ms-channel-id", + "createdDateTime": time.Now().Format(time.RFC3339), + "displayName": "test channel", + "description": "Test channel", + "membershipType": "standard", + }) + require.NoError(t, err) + + err = mockClient.MockError(http.MethodPost, "/v1.0/teams/ms-team-id/channels/ms-channel-id/messages") + require.NoError(t, err) + + require.Eventually(t, func() bool { + _, _, err = client.ExecuteCommand(context.Background(), channel.Id, "/msteams link ms-team-id ms-channel-id") + return err == nil + }, 5*time.Second, 500*time.Millisecond) + + newPost, _, err := client.CreatePost(context.Background(), &post) + require.NoError(t, err) + + require.Eventually(t, func() bool { + var logs string + logs, err = mattermost.GetLogs(context.Background(), 10) + if err != nil { + return false + } + if strings.Contains(logs, "Error creating post on MS Teams") && strings.Contains(logs, "Test bad request") { + return true + } + return false + }, 1*time.Second, 50*time.Millisecond) + + _, err = store.GetPostInfoByMattermostID(newPost.Id) + require.Error(t, err) + }) +} + +func TestMessageHasBeenPostedNewDirectMessageE2E(t *testing.T) { + t.Parallel() + + mattermost, store, mockClient, tearDown := containere2e.NewE2ETestPlugin(t) + defer tearDown() + + client, err := mattermost.GetAdminClient(context.Background()) + require.NoError(t, err) + + user, _, err := client.GetMe(context.Background(), "") + require.NoError(t, err) + + err = mattermost.CreateUser(context.Background(), "otheruser@mattermost.com", "otheruser", "password") + require.NoError(t, err) + + err = mattermost.AddUserToTeam(context.Background(), "otheruser", "test") + require.NoError(t, err) + + otherUser, _, err := client.GetUserByUsername(context.Background(), "otheruser", "") + require.NoError(t, err) + + dm, _, err := client.CreateDirectChannel(context.Background(), user.Id, otherUser.Id) + require.NoError(t, err) + + post := model.Post{ + CreateAt: model.GetMillis(), + UpdateAt: model.GetMillis(), + UserId: user.Id, + ChannelId: dm.Id, + Message: "message", + } + + err = store.SetUserInfo(user.Id, "ms-user-id", &fakeToken) + require.NoError(t, err) + err = store.SetUserInfo(otherUser.Id, "ms-otheruser-id", nil) + require.NoError(t, err) + + t.Run("Everything OK", func(t *testing.T) { + if err = mockClient.Reset(); err != nil { + t.Log(err) + } + + err = mockClient.Post("/v1.0/chats", map[string]any{ + "id": "ms-dm-id", + "createdDateTime": time.Now().Format(time.RFC3339), + "displayName": "test channel", + "description": "Test channel", + "membershipType": "oneOnOne", + }) + require.NoError(t, err) + + err = mockClient.Get("/v1.0/chats/ms-dm-id", map[string]any{ + "id": "ms-dm-id", + "createdDateTime": time.Now().Format(time.RFC3339), + "displayName": "test channel", + "description": "Test channel", + "membershipType": "oneOnOne", + }) + require.NoError(t, err) + + newPostID := model.NewId() + err = mockClient.Post("/v1.0/chats/ms-dm-id/messages", map[string]any{ + "id": newPostID, + "etag": "1616990032035", + "messageType": "message", + "createdDateTime": time.Now().Format(time.RFC3339), + "lastModifiedDateTime": time.Now().Format(time.RFC3339), + "importance": "normal", + "locale": "en-us", + "webUrl": "https://teams.microsoft.com/l/message/ms-dm-id/test-message-id", + "from": map[string]any{ + "user": map[string]any{ + "@odata.type": "#microsoft.graph.teamworkUserIdentity", + "id": "ms-user-id", + "displayName": user.Username, + "userIdentityType": "aadUser", + "tenantId": "tenant-id", + }, + }, + "body": map[string]any{ + "contentType": "text", + "content": "Hello World", + }, + "channelIdentity": map[string]any{ + "channelId": "ms-dm-id", + }, + }) + require.NoError(t, err) + + var newPost *model.Post + newPost, _, err = client.CreatePost(context.Background(), &post) + require.NoError(t, err) + + require.Eventually(t, func() bool { + var postInfo *storemodels.PostInfo + postInfo, err = store.GetPostInfoByMattermostID(newPost.Id) + if err != nil { + return false + } + if postInfo.MSTeamsID == newPostID { + return true + } + return false + }, 1*time.Second, 50*time.Millisecond) + }) + + t.Run("Failing to deliver message to MSTeams", func(t *testing.T) { + if err = mockClient.Reset(); err != nil { + t.Log(err) + } + + err = mockClient.Post("/v1.0/chats", map[string]any{ + "id": "ms-dm-id", + "createdDateTime": time.Now().Format(time.RFC3339), + "displayName": "test channel", + "description": "Test channel", + "membershipType": "oneOnOne", + }) + require.NoError(t, err) + + err = mockClient.Get("/v1.0/chats/ms-dm-id", map[string]any{ + "id": "ms-dm-id", + "createdDateTime": time.Now().Format(time.RFC3339), + "displayName": "test channel", + "description": "Test channel", + "membershipType": "oneOnOne", + }) + require.NoError(t, err) - _, _, err = client.ExecuteCommand(context.Background(), channel.Id, "/msteams link ms-team-id ms-channel-id") + err = mockClient.MockError(http.MethodPost, "/v1.0/chats/ms-dm-id/messages") require.NoError(t, err) newPost, _, err := client.CreatePost(context.Background(), &post) @@ -95,7 +305,7 @@ func TestMessageHasBeenPostedNewMessageE2E(t *testing.T) { if err != nil { return false } - if strings.Contains(logs, "Error creating post on MS Teams") && strings.Contains(logs, "Unable to send the message") { + if strings.Contains(logs, "Error creating post on MS Teams") && strings.Contains(logs, "Test bad request") { return true } return false diff --git a/server/command.go b/server/command.go index 27078d553..ac50c6419 100644 --- a/server/command.go +++ b/server/command.go @@ -148,7 +148,10 @@ func (p *Plugin) ExecuteCommand(_ *plugin.Context, args *model.CommandArgs) (*mo return p.executePromoteUserCommand(args, parameters) } - return p.cmdError(args.UserId, args.ChannelId, "Unknown command. Valid options: link, unlink and show.") + if p.getConfiguration().SyncLinkedChannels { + return p.cmdError(args.UserId, args.ChannelId, "Unknown command. Valid options: link, unlink, show, show-links, connect, connect-bot, disconnect, disconnect-bot and promote.") + } + return p.cmdError(args.UserId, args.ChannelId, "Unknown command. Valid options: connect, connect-bot, disconnect, disconnect-bot and promote.") } func (p *Plugin) executeLinkCommand(args *model.CommandArgs, parameters []string) (*model.CommandResponse, *model.AppError) { diff --git a/server/e2e/main_test.go b/server/e2e/main_test.go index 8320c48b3..0e3ab8bb1 100644 --- a/server/e2e/main_test.go +++ b/server/e2e/main_test.go @@ -6,11 +6,15 @@ package main import ( "context" "encoding/json" + "fmt" "os" "testing" + "github.com/Azure/azure-sdk-for-go/sdk/azidentity" "github.com/mattermost/mattermost-plugin-msteams/server/msteams" "github.com/mattermost/mattermost/server/public/model" + pluginapi "github.com/mattermost/mattermost/server/public/pluginapi" + msgraphsdk "github.com/microsoftgraph/msgraph-sdk-go" "github.com/stretchr/testify/require" ) @@ -38,6 +42,28 @@ var mmClient *model.Client4 var mmClientAdmin *model.Client4 var testCfg *TestConfig +func newManualClient(tenantID, clientID string, logService *pluginapi.LogService) msteams.Client { + cred, err := azidentity.NewDeviceCodeCredential(&azidentity.DeviceCodeCredentialOptions{ + TenantID: tenantID, + ClientID: clientID, + UserPrompt: func(ctx context.Context, message azidentity.DeviceCodeMessage) error { + fmt.Println(message.Message) + return nil + }, + }) + if err != nil { + fmt.Printf("Error creating credentials: %v\n", err) + return nil + } + + client, err := msgraphsdk.NewGraphServiceClientWithCredentials(cred, msteams.TeamsDefaultScopes) + if err != nil { + fmt.Printf("Error creating client: %v\n", err) + return nil + } + return msteams.NewManualClient(tenantID, clientID, logService, client) +} + func setup(t *testing.T) { if testCfg == nil { data, err := os.ReadFile("testconfig.json") @@ -51,7 +77,7 @@ func setup(t *testing.T) { } if msClient == nil { - msClient = msteams.NewManualClient(testCfg.MSTeams.TenantID, testCfg.MSTeams.ClientID, nil) + msClient = newManualClient(testCfg.MSTeams.TenantID, testCfg.MSTeams.ClientID, nil) } if mmClient == nil { mmClient = model.NewAPIv4Client(testCfg.Mattermost.URL) diff --git a/server/msteams/client.go b/server/msteams/client.go index 6ff1f67d3..95a0902ef 100644 --- a/server/msteams/client.go +++ b/server/msteams/client.go @@ -177,7 +177,7 @@ func (at AccessToken) GetToken(_ context.Context, _ policy.TokenRequestOptions) }, nil } -var teamsDefaultScopes = []string{"https://graph.microsoft.com/.default"} +var TeamsDefaultScopes = []string{"https://graph.microsoft.com/.default"} func NewApp(tenantID, clientID, clientSecret string, logService *pluginapi.LogService) Client { return &ClientImpl{ @@ -190,36 +190,15 @@ func NewApp(tenantID, clientID, clientSecret string, logService *pluginapi.LogSe } } -func NewManualClient(tenantID, clientID string, logService *pluginapi.LogService) Client { - c := &ClientImpl{ +func NewManualClient(tenantID, clientID string, logService *pluginapi.LogService, client *msgraphsdk.GraphServiceClient) Client { + return &ClientImpl{ ctx: context.Background(), clientType: "token", tenantID: tenantID, clientID: clientID, logService: logService, - client: nil, - } - - cred, err := azidentity.NewDeviceCodeCredential(&azidentity.DeviceCodeCredentialOptions{ - TenantID: tenantID, - ClientID: clientID, - UserPrompt: func(ctx context.Context, message azidentity.DeviceCodeMessage) error { - fmt.Println(message.Message) - return nil - }, - }) - if err != nil { - fmt.Printf("Error creating credentials: %v\n", err) - return nil - } - - client, err := msgraphsdk.NewGraphServiceClientWithCredentials(cred, teamsDefaultScopes) - if err != nil { - fmt.Printf("Error creating client: %v\n", err) - return nil + client: client, } - c.client = client - return c } func NewTokenClient(redirectURL, tenantID, clientID, clientSecret string, token *oauth2.Token, logService *pluginapi.LogService) Client { @@ -237,7 +216,7 @@ func NewTokenClient(redirectURL, tenantID, clientID, clientSecret string, token conf := &oauth2.Config{ ClientID: clientID, ClientSecret: clientSecret, - Scopes: append(teamsDefaultScopes, "offline_access"), + Scopes: append(TeamsDefaultScopes, "offline_access"), Endpoint: oauth2.Endpoint{ AuthURL: fmt.Sprintf("https://login.microsoftonline.com/%s/oauth2/v2.0/authorize", tenantID), TokenURL: fmt.Sprintf("https://login.microsoftonline.com/%s/oauth2/v2.0/token", tenantID), @@ -245,22 +224,22 @@ func NewTokenClient(redirectURL, tenantID, clientID, clientSecret string, token RedirectURL: redirectURL, } + httpClient := getHTTPClient() + accessToken := AccessToken{tokenSource: conf.TokenSource(context.Background(), client.token)} - auth, err := a.NewAzureIdentityAuthenticationProviderWithScopes(accessToken, append(teamsDefaultScopes, "offline_access")) + auth, err := a.NewAzureIdentityAuthenticationProviderWithScopes(accessToken, append(TeamsDefaultScopes, "offline_access")) if err != nil { logService.Error("Unable to create the client from the token", "error", err) return nil } - adapter, err := msgraphsdk.NewGraphRequestAdapter(auth) + adapter, err := msgraphsdk.NewGraphRequestAdapterWithParseNodeFactoryAndSerializationWriterFactoryAndHttpClient(auth, nil, nil, httpClient) if err != nil { logService.Error("Unable to create the client from the token", "error", err) return nil } - clientMutex.Lock() - defer clientMutex.Unlock() client.client = msgraphsdk.NewGraphServiceClient(&ConcurrentGraphRequestAdapter{GraphRequestAdapter: *adapter}) return client @@ -270,7 +249,7 @@ func (tc *ClientImpl) RefreshToken(token *oauth2.Token) (*oauth2.Token, error) { conf := &oauth2.Config{ ClientID: tc.clientID, ClientSecret: tc.clientSecret, - Scopes: append(teamsDefaultScopes, "offline_access"), + Scopes: append(TeamsDefaultScopes, "offline_access"), Endpoint: oauth2.Endpoint{ AuthURL: fmt.Sprintf("https://login.microsoftonline.com/%s/oauth2/v2.0/authorize", tc.tenantID), TokenURL: fmt.Sprintf("https://login.microsoftonline.com/%s/oauth2/v2.0/token", tc.tenantID), @@ -316,6 +295,7 @@ func (tc *ClientImpl) Connect() error { RetryDelay: 4 * time.Second, MaxRetryDelay: 120 * time.Second, }, + Transport: getAuthClient(), }, }, ) @@ -327,11 +307,21 @@ func (tc *ClientImpl) Connect() error { return errors.New("not valid client type, this shouldn't happen ever") } - client, err := msgraphsdk.NewGraphServiceClientWithCredentials(cred, teamsDefaultScopes) + httpClient := getHTTPClient() + + auth, err := a.NewAzureIdentityAuthenticationProviderWithScopes(cred, append(TeamsDefaultScopes, "offline_access")) if err != nil { return err } - tc.client = client + + adapter, err := msgraphsdk.NewGraphRequestAdapterWithParseNodeFactoryAndSerializationWriterFactoryAndHttpClient(auth, nil, nil, httpClient) + if err != nil { + return err + } + + clientMutex.Lock() + defer clientMutex.Unlock() + tc.client = msgraphsdk.NewGraphServiceClient(&ConcurrentGraphRequestAdapter{GraphRequestAdapter: *adapter}) return nil } @@ -2108,7 +2098,7 @@ func GetAuthURL(redirectURL string, tenantID string, clientID string, clientSecr conf := &oauth2.Config{ ClientID: clientID, ClientSecret: clientSecret, - Scopes: append(teamsDefaultScopes, "offline_access"), + Scopes: append(TeamsDefaultScopes, "offline_access"), Endpoint: oauth2.Endpoint{ AuthURL: fmt.Sprintf("https://login.microsoftonline.com/%s/oauth2/v2.0/authorize", tenantID), TokenURL: fmt.Sprintf("https://login.microsoftonline.com/%s/oauth2/v2.0/token", tenantID), diff --git a/server/msteams/client_mock.go b/server/msteams/client_mock.go new file mode 100644 index 000000000..69951d72a --- /dev/null +++ b/server/msteams/client_mock.go @@ -0,0 +1,46 @@ +//go:build msteamsMock +// +build msteamsMock + +package msteams + +import ( + "crypto/tls" + "net/http" + "net/url" + + khttp "github.com/microsoft/kiota-http-go" + msgraphsdk "github.com/microsoftgraph/msgraph-sdk-go" + msgraphcore "github.com/microsoftgraph/msgraph-sdk-go-core" +) + +func getAuthClient() *http.Client { + proxyAddress := "http://mockserver:1080" + proxyUrl, _ := url.Parse(proxyAddress) + // Setup proxy for the token credential from azidentity + authClient := &http.Client{ + Transport: &http.Transport{ + Proxy: http.ProxyURL(proxyUrl), + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + }, + } + return authClient +} + +func getHTTPClient() *http.Client { + proxyAddress := "http://mockserver:1080" + proxyUrl, _ := url.Parse(proxyAddress) + + // Get default middleware from SDK + defaultClientOptions := msgraphsdk.GetDefaultClientOptions() + defaultMiddleWare := msgraphcore.GetDefaultMiddlewaresWithOptions(&defaultClientOptions) + + transport := khttp.NewCustomTransportWithParentTransport(&http.Transport{ + Proxy: http.ProxyURL(proxyUrl), + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + }, defaultMiddleWare...) + + // Create an HTTP client with the middleware + httpClient, _ := khttp.GetClientWithProxySettings(proxyAddress, defaultMiddleWare...) + httpClient.Transport = transport + return httpClient +} diff --git a/server/msteams/client_nomock.go b/server/msteams/client_nomock.go new file mode 100644 index 000000000..cc7706acb --- /dev/null +++ b/server/msteams/client_nomock.go @@ -0,0 +1,23 @@ +//go:build !msteamsMock +// +build !msteamsMock + +package msteams + +import ( + "net/http" + + khttp "github.com/microsoft/kiota-http-go" + msgraphsdk "github.com/microsoftgraph/msgraph-sdk-go" + msgraphcore "github.com/microsoftgraph/msgraph-sdk-go-core" +) + +func getAuthClient() *http.Client { + return http.DefaultClient +} + +func getHTTPClient() *http.Client { + defaultClientOptions := msgraphsdk.GetDefaultClientOptions() + defaultMiddleWare := msgraphcore.GetDefaultMiddlewaresWithOptions(&defaultClientOptions) + + return khttp.GetDefaultClient(defaultMiddleWare...) +} diff --git a/server/plugin.go b/server/plugin.go index b20c85040..83d87435b 100644 --- a/server/plugin.go +++ b/server/plugin.go @@ -233,12 +233,6 @@ func (p *Plugin) connectTeamsAppClient() error { return nil } - clientMock := getClientMock(p) - if clientMock != nil { - p.msteamsAppClient = clientMock - return nil - } - msteamsAppClient := msteams.NewApp( p.getConfiguration().TenantID, p.getConfiguration().ClientID, @@ -336,6 +330,7 @@ func (p *Plugin) start(isRestart bool) { if err = p.API.RegisterCommand(p.createCommand(p.getConfiguration().SyncLinkedChannels)); err != nil { p.API.LogError("Failed to register command", "error", err) } + p.API.LogDebug("plugin started") } func (p *Plugin) getBase64Certificate() string { @@ -437,13 +432,7 @@ func (p *Plugin) generatePluginSecrets() error { func (p *Plugin) OnActivate() error { if p.clientBuilderWithToken == nil { - if getClientMock(p) != nil { - p.clientBuilderWithToken = func(string, string, string, string, *oauth2.Token, *pluginapi.LogService) msteams.Client { - return getClientMock(p) - } - } else { - p.clientBuilderWithToken = msteams.NewTokenClient - } + p.clientBuilderWithToken = msteams.NewTokenClient } err := p.generatePluginSecrets() if err != nil { diff --git a/server/testutils/containere2e/apimock.go b/server/testutils/containere2e/apimock.go new file mode 100644 index 000000000..b2d757daa --- /dev/null +++ b/server/testutils/containere2e/apimock.go @@ -0,0 +1,192 @@ +package containere2e + +import ( + "bytes" + "encoding/json" + "net/http" + "time" + + "github.com/google/uuid" +) + +type MockClient struct { + api string +} + +func NewMockClient(api string) (*MockClient, error) { + mock := &MockClient{api: api} + if err := mock.init(); err != nil { + return nil, err + } + return mock, nil +} + +func (m *MockClient) init() error { + err := m.Get("/common/discovery/instance", map[string]any{ + "tenant_discovery_endpoint": "https://login.microsoftonline.com/d2888234-d303-4c94-8f45-c7348f089048/v2.0/.well-known/openid-configuration", + "api-version": "1.1", + "metadata": []map[string]any{ + {"preferred_network": "login.microsoftonline.com", "preferred_cache": "login.windows.net", "aliases": []string{"login.microsoftonline.com", "login.windows.net", "login.microsoft.com", "sts.windows.net"}}, + {"preferred_network": "login.partner.microsoftonline.cn", "preferred_cache": "login.partner.microsoftonline.cn", "aliases": []string{"login.partner.microsoftonline.cn", "login.chinacloudapi.cn"}}, + {"preferred_network": "login.microsoftonline.de", "preferred_cache": "login.microsoftonline.de", "aliases": []string{"login.microsoftonline.de"}}, + {"preferred_network": "login.microsoftonline.us", "preferred_cache": "login.microsoftonline.us", "aliases": []string{"login.microsoftonline.us", "login.usgovcloudapi.net"}}, + {"preferred_network": "login-us.microsoftonline.com", "preferred_cache": "login-us.microsoftonline.com", "aliases": []string{"login-us.microsoftonline.com"}}, + }, + }) + if err != nil { + return err + } + + err = m.Get("/tenant-id/v2.0/.well-known/openid-configuration", map[string]any{ + "token_endpoint": "https://login.microsoftonline.com/d2888234-d303-4c94-8f45-c7348f089048/oauth2/v2.0/token", + "token_endpoint_auth_methods_supported": []string{"client_secret_post", "private_key_jwt", "client_secret_basic"}, + "jwks_uri": "https://login.microsoftonline.com/d2888234-d303-4c94-8f45-c7348f089048/discovery/v2.0/keys", + "response_modes_supported": []string{"query", "fragment", "form_post"}, + "subject_types_supported": []string{"pairwise"}, + "id_token_signing_alg_values_supported": []string{"RS256"}, + "response_types_supported": []string{"code", "id_token", "code id_token", "id_token token"}, + "scopes_supported": []string{"openid", "profile", "email", "offline_access"}, + "issuer": "https://login.microsoftonline.com/d2888234-d303-4c94-8f45-c7348f089048/v2.0", + "request_uri_parameter_supported": false, + "userinfo_endpoint": "https://graph.microsoft.com/oidc/userinfo", + "authorization_endpoint": "https://login.microsoftonline.com/d2888234-d303-4c94-8f45-c7348f089048/oauth2/v2.0/authorize", + "device_authorization_endpoint": "https://login.microsoftonline.com/d2888234-d303-4c94-8f45-c7348f089048/oauth2/v2.0/devicecode", + "http_logout_supported": true, + "frontchannel_logout_supported": true, + "end_session_endpoint": "https://login.microsoftonline.com/d2888234-d303-4c94-8f45-c7348f089048/oauth2/v2.0/logout", + "claims_supported": []string{"sub", "iss", "cloud_instance_name", "cloud_instance_host_name", "cloud_graph_host_name", "msgraph_host", "aud", "exp", "iat", "auth_time", "acr", "nonce", "preferred_username", "name", "tid", "ver", "at_hash", "c_hash", "email"}, + "kerberos_endpoint": "https://login.microsoftonline.com/d2888234-d303-4c94-8f45-c7348f089048/kerberos", + "tenant_region_scope": "NA", + "cloud_instance_name": "microsoftonline.com", + "cloud_graph_host_name": "graph.windows.net", + "msgraph_host": "graph.microsoft.com", + "rbac_url": "https://pas.windows.net", + }) + if err != nil { + return err + } + + err = m.Post("/d2888234-d303-4c94-8f45-c7348f089048/oauth2/v2.0/token", map[string]any{ + "token_type": "Bearer", + "expires_in": 3599, + "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c", + }) + if err != nil { + return err + } + + err = m.Get("/v1.0/subscriptions", map[string]any{}) + if err != nil { + return err + } + + err = m.Post("/v1.0/subscriptions", map[string]any{ + "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#subscriptions/$entity", + "id": uuid.New().String(), + "resource": "/test", + "applicationId": "application-id", + "changeType": "created", + "clientState": "secretClientValue", + "notificationUrl": "https://webhook.azurewebsites.net/api/send/myNotifyClient", + "expirationDateTime": "2036-11-20T18:23:45.9356913Z", + "creatorId": "8ee44408-0679-472c-bc2a-692812af3437", + "latestSupportedTlsVersion": "v1_2", + "notificationContentType": "application/json", + }) + if err != nil { + return err + } + + err = m.Get("/v1.0/applications(appId='client-id')", map[string]any{ + "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#applications/$entity", + "id": "client-id", + }) + if err != nil { + return err + } + + return nil +} + +func (m *MockClient) Get(url string, body map[string]any) error { + return m.Mock(http.MethodGet, url, http.StatusOK, body) +} + +func (m *MockClient) Post(url string, body map[string]any) error { + return m.Mock(http.MethodPost, url, http.StatusOK, body) +} + +func (m *MockClient) Put(url string, body map[string]any) error { + return m.Mock(http.MethodPut, url, http.StatusOK, body) +} + +func (m *MockClient) Delete(url string, body map[string]any) error { + return m.Mock(http.MethodDelete, url, http.StatusOK, body) +} + +func (m *MockClient) MockError(method string, url string) error { + return m.Mock(method, url, 401, map[string]any{ + "error": map[string]any{ + "code": "badRequest", + "message": "Test bad request", + "innerError": map[string]any{ + "code": "invalidRange", + "request-id": "request-id", + "date": time.Now().Format(time.RFC3339), + }, + }, + }) +} + +func (m *MockClient) Mock(method string, url string, statusCode int, body map[string]any) error { + bodyData, err := json.Marshal(body) + if err != nil { + return err + } + mockExpectation := map[string]any{ + "priority": 0, + "httpRequest": map[string]any{ + "method": method, + "path": url, + }, + "httpResponse": map[string]any{ + "statusCode": statusCode, + "body": map[string]any{ + "type": "STRING", + "contentType": "application/json", + "string": string(bodyData), + }, + }, + } + + testMock, err := json.Marshal(mockExpectation) + if err != nil { + return err + } + req, err := http.NewRequest("PUT", m.api+"/mockserver/expectation", bytes.NewReader(testMock)) + if err != nil { + return err + } + req.Header.Set("Content-Type", "application/json") + resp, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + return nil +} + +func (m *MockClient) Reset() error { + req, err := http.NewRequest("PUT", m.api+"/mockserver/reset", nil) + if err != nil { + return err + } + req.Header.Set("Content-Type", "application/json") + resp, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + return m.init() +} diff --git a/server/testutils/containere2e/containere2e.go b/server/testutils/containere2e/containere2e.go index 48bbaf709..4198d1be3 100644 --- a/server/testutils/containere2e/containere2e.go +++ b/server/testutils/containere2e/containere2e.go @@ -2,21 +2,29 @@ package containere2e import ( "context" - "encoding/json" - "net/http" "os" "os/exec" "path/filepath" + "strings" "sync" "testing" "github.com/mattermost/mattermost-plugin-msteams/server/store/sqlstore" "github.com/mattermost/mattermost-plugin-msteams/server/testutils/mmcontainer" - "github.com/mattermost/mattermost-plugin-msteams/server/testutils/testmodels" - "github.com/mattermost/mattermost/server/public/model" "github.com/stretchr/testify/require" + "github.com/testcontainers/testcontainers-go" + "github.com/testcontainers/testcontainers-go/modules/mockserver" + "github.com/testcontainers/testcontainers-go/network" ) +type tLogConsumer struct { + t *testing.T +} + +func (tlc *tLogConsumer) Accept(log testcontainers.Log) { + tlc.t.Log(strings.TrimSpace(string(log.Content))) +} + var buildPluginOnce sync.Once func buildPlugin(t *testing.T) { @@ -24,7 +32,7 @@ func buildPlugin(t *testing.T) { cmd.Env = os.Environ() cmd.Env = append(cmd.Env, "DEFAULT_GOOS=linux") cmd.Env = append(cmd.Env, "DEFAULT_GOARCH=amd64") - cmd.Env = append(cmd.Env, "GO_BUILD_TAGS=clientMock") + cmd.Env = append(cmd.Env, "GO_BUILD_TAGS=msteamsMock") cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr err := cmd.Run() @@ -33,27 +41,64 @@ func buildPlugin(t *testing.T) { type Option func(*mmcontainer.MattermostContainer) +func WithEnv(key string, value string) testcontainers.CustomizeRequestOption { + return func(req *testcontainers.GenericContainerRequest) { + req.Env[key] = value + } +} + func WithoutLicense() mmcontainer.MattermostCustomizeRequestOption { return func(req *mmcontainer.MattermostContainerRequest) { mmcontainer.WithEnv("MM_LICENSE", "")(req) } } -func NewE2ETestPlugin(t *testing.T, extraOptions ...mmcontainer.MattermostCustomizeRequestOption) (*mmcontainer.MattermostContainer, *sqlstore.SQLStore, func()) { +func NewE2ETestPlugin(t *testing.T, extraOptions ...mmcontainer.MattermostCustomizeRequestOption) (*mmcontainer.MattermostContainer, *sqlstore.SQLStore, *MockClient, func()) { buildPluginOnce.Do(func() { buildPlugin(t) }) + newNetwork, err := network.New(context.Background(), network.WithCheckDuplicate()) + if err != nil { + t.Fatal(err) + } + ctx := context.Background() matches, err := filepath.Glob("../../dist/*.tar.gz") if err != nil { + _ = newNetwork.Remove(context.Background()) t.Fatal(err) } if len(matches) == 0 { + _ = newNetwork.Remove(context.Background()) t.Fatal("Unable to find plugin tar.gz file") } filename := matches[0] + mockserverContainer, err := mockserver.RunContainer( + context.Background(), + network.WithNetwork([]string{"mockserver"}, newNetwork), + WithEnv("MOCKSERVER_ATTEMPT_TO_PROXY_IF_NO_MATCHING_EXPECTATION", "false"), + ) + if err != nil { + _ = newNetwork.Remove(context.Background()) + t.Fatal(err) + } + + mockAPIURL, err := mockserverContainer.URL(context.Background()) + if err != nil { + _ = mockserverContainer.Terminate(context.Background()) + _ = newNetwork.Remove(context.Background()) + t.Fatal(err) + } + + mockClient, err := NewMockClient(mockAPIURL) + if err != nil { + _ = mockserverContainer.Terminate(context.Background()) + _ = newNetwork.Remove(context.Background()) + t.Fatal(err) + } + pluginConfig := map[string]any{ "clientid": "client-id", "clientsecret": "client-secret", @@ -63,58 +108,47 @@ func NewE2ETestPlugin(t *testing.T, extraOptions ...mmcontainer.MattermostCustom "maxsizeforcompletedownload": 20, "tenantid": "tenant-id", "webhooksecret": "webhook-secret", + "syncdirectmessages": true, "synclinkedchannels": true, } options := []mmcontainer.MattermostCustomizeRequestOption{ mmcontainer.WithPlugin(filename, "com.mattermost.msteams-sync", pluginConfig), - mmcontainer.WithEnv("MM_MSTEAMSSYNC_MOCK_CLIENT", "true"), - mmcontainer.WithTestingLogConsumer(t), + mmcontainer.WithLogConsumers(&tLogConsumer{t: t}), + mmcontainer.WithNetwork(newNetwork), } options = append(options, extraOptions...) - mattermost, err := mmcontainer.RunContainer(ctx, options...) - require.NoError(t, err) - // TODO: This won't be required after jespino gets https://github.com/testcontainers/testcontainers-go/pull/2073 merged. - err = mattermost.StartLogProducer(ctx) require.NoError(t, err) - t.Cleanup(func() { - err = mattermost.StopLogProducer() - require.NoError(t, err) - }) + if err != nil { + _ = mockserverContainer.Terminate(context.Background()) + _ = mattermost.Terminate(ctx) + _ = newNetwork.Remove(context.Background()) + t.Fatal(err) + } conn, err := mattermost.PostgresConnection(ctx) if err != nil { + _ = mockserverContainer.Terminate(ctx) _ = mattermost.Terminate(ctx) + _ = newNetwork.Remove(context.Background()) } require.NoError(t, err) store := sqlstore.New(conn, nil, func() []string { return []string{""} }, func() []byte { return []byte("eyPBz0mBhwfGGwce9hp4TWaYzgY7MdIB") }) if err2 := store.Init(); err2 != nil { + _ = mockserverContainer.Terminate(ctx) _ = mattermost.Terminate(ctx) + _ = newNetwork.Remove(context.Background()) } require.NoError(t, err) tearDown := func() { + require.NoError(t, mockserverContainer.Terminate(context.Background())) require.NoError(t, mattermost.Terminate(context.Background())) + require.NoError(t, newNetwork.Remove(context.Background())) } - return mattermost, store, tearDown -} - -func MockMSTeamsClient(t *testing.T, client *model.Client4, method string, returnType string, returns interface{}, returnErr string) { - mockStruct := testmodels.MockCallReturns{ReturnType: returnType, Returns: returns, Err: returnErr} - mockData, err := json.Marshal(mockStruct) - require.NoError(t, err) - - resp, err := client.DoAPIRequest(context.Background(), http.MethodPost, client.URL+"/plugins/com.mattermost.msteams-sync/add-mock/"+method, string(mockData), "") - require.NoError(t, err) - resp.Body.Close() -} - -func ResetMSTeamsClientMock(t *testing.T, client *model.Client4) { - resp, err := client.DoAPIRequest(context.Background(), http.MethodPost, client.URL+"/plugins/com.mattermost.msteams-sync/reset-mocks", "", "") - require.NoError(t, err) - resp.Body.Close() + return mattermost, store, mockClient, tearDown } diff --git a/server/testutils/mmcontainer/testscontainer.go b/server/testutils/mmcontainer/testscontainer.go index 5ae175fff..0343fbd9f 100644 --- a/server/testutils/mmcontainer/testscontainer.go +++ b/server/testutils/mmcontainer/testscontainer.go @@ -7,7 +7,6 @@ import ( "fmt" "io" "net" - "testing" "time" "github.com/google/uuid" @@ -25,7 +24,7 @@ const ( defaultPassword = "admin" defaultTeamName = "test" defaultTeamDisplayName = "Test" - defaultMattermostImage = "mattermost/mattermost-enterprise-edition" + defaultMattermostImage = "mattermost/mattermost-enterprise-edition:release-9.6" dbconn = "postgres://user:pass@db:5432/mattermost_test?sslmode=disable" ) @@ -46,16 +45,18 @@ type MattermostContainerRequest struct { teamDisplayName string plugins []plugin config *model.Config - logConsumer testcontainers.LogConsumer + network *testcontainers.DockerNetwork + customNetwork bool } // MattermostContainer represents the mattermost container type used in the module type MattermostContainer struct { testcontainers.Container - pgContainer *postgres.PostgresContainer - network *testcontainers.DockerNetwork - username string - password string + pgContainer *postgres.PostgresContainer + network *testcontainers.DockerNetwork + customNetwork bool + username string + password string } // URL returns the url of the mattermost instance @@ -127,8 +128,10 @@ func (c *MattermostContainer) Terminate(ctx context.Context) error { errors = fmt.Errorf("%w + %w", errors, err) } - if err := c.network.Remove(ctx); err != nil { - errors = fmt.Errorf("%w + %w", errors, err) + if !c.customNetwork { + if err := c.network.Remove(ctx); err != nil { + errors = fmt.Errorf("%w + %w", errors, err) + } } return errors @@ -331,6 +334,16 @@ func WithTeam(teamName, teamDisplayName string) MattermostCustomizeRequestOption } } +// WithNetwork sets the network for the mattermost instance +func WithNetwork(nw *testcontainers.DockerNetwork) MattermostCustomizeRequestOption { + return func(req *MattermostContainerRequest) { + req.Networks = []string{nw.Name} + req.NetworkAliases = map[string][]string{nw.Name: {"mattermost"}} + req.network = nw + req.customNetwork = true + } +} + // WithPlugin sets the plugin to be installed in the mattermost instance func WithPlugin(pluginPath, pluginID string, pluginConfig map[string]any) MattermostCustomizeRequestOption { return func(req *MattermostContainerRequest) { @@ -351,18 +364,14 @@ func WithPlugin(pluginPath, pluginID string, pluginConfig map[string]any) Matter } } -type tLogConsumer struct { - t *testing.T -} - -func (tlc *tLogConsumer) Accept(log testcontainers.Log) { - tlc.t.Log(string(log.Content)) -} - -// WithTestingLogConsumer pipes logs to the given testing instance. -func WithTestingLogConsumer(t *testing.T) MattermostCustomizeRequestOption { +// WithLogConsumers sets the log consumers for a container +func WithLogConsumers(consumer ...testcontainers.LogConsumer) MattermostCustomizeRequestOption { return func(req *MattermostContainerRequest) { - req.logConsumer = &tLogConsumer{t} + if req.LogConsumerCfg == nil { + req.LogConsumerCfg = &testcontainers.LogConsumerConfig{} + } + + req.LogConsumerCfg.Consumers = consumer } } @@ -383,19 +392,6 @@ func runPostgresContainer(ctx context.Context, nw *testcontainers.DockerNetwork) // RunContainer creates an instance of the mattermost container type func RunContainer(ctx context.Context, opts ...MattermostCustomizeRequestOption) (*MattermostContainer, error) { - newNetwork, err := network.New(ctx, network.WithCheckDuplicate()) - if err != nil { - return nil, err - } - - postgresContainer, err := runPostgresContainer(ctx, newNetwork) - if err != nil { - if err2 := newNetwork.Remove(ctx); err2 != nil { - err = fmt.Errorf("%w + %w", err, err2) - } - return nil, err - } - req := MattermostContainerRequest{ GenericContainerRequest: testcontainers.GenericContainerRequest{ ContainerRequest: testcontainers.ContainerRequest{ @@ -410,6 +406,7 @@ func RunContainer(ctx context.Context, opts ...MattermostCustomizeRequestOption) "MM_FILESETTINGS_MAXFILESIZE": "256000000", "MM_LOGSETTINGS_CONSOLELEVEL": "DEBUG", "MM_LOGSETTINGS_ENABLEFILE": "true", + "MM_EXPERIMENTALSETTINGS_ENABLESHAREDCHANNELS": "true", "MM_SERVICEENVIRONMENT": model.ServiceEnvironmentTest, "MM_LICENSE": "eyJpZCI6InVjR1kycGNmcjVGSzgwTko5SGVuemhmWDZmIiwiaXNzdWVkX2F0IjoxNzA2OTAyMTE1NTU2LCJzdGFydHNfYXQiOjE3MDY5MDIxMTU1NTYsImV4cGlyZXNfYXQiOjE3NzAwMDg0MDAwMDAsInNrdV9uYW1lIjoiRW50ZXJwcmlzZSIsInNrdV9zaG9ydF9uYW1lIjoiZW50ZXJwcmlzZSIsImN1c3RvbWVyIjp7ImlkIjoicDl1bjM2OWE2N2hpbWo0eWQ2aTZpYjM5YmgiLCJuYW1lIjoiTWF0dGVybW9zdCBFMkUgVGVzdHMiLCJlbWFpbCI6Implc3NlQG1hdHRlcm1vc3QuY29tIiwiY29tcGFueSI6Ik1hdHRlcm1vc3QgRTJFIFRlc3RzIn0sImZlYXR1cmVzIjp7InVzZXJzIjoxMDAsImxkYXAiOnRydWUsImxkYXBfZ3JvdXBzIjp0cnVlLCJtZmEiOnRydWUsImdvb2dsZV9vYXV0aCI6dHJ1ZSwib2ZmaWNlMzY1X29hdXRoIjp0cnVlLCJjb21wbGlhbmNlIjp0cnVlLCJjbHVzdGVyIjp0cnVlLCJtZXRyaWNzIjp0cnVlLCJtaHBucyI6dHJ1ZSwic2FtbCI6dHJ1ZSwiZWxhc3RpY19zZWFyY2giOnRydWUsImFubm91bmNlbWVudCI6dHJ1ZSwidGhlbWVfbWFuYWdlbWVudCI6dHJ1ZSwiZW1haWxfbm90aWZpY2F0aW9uX2NvbnRlbnRzIjp0cnVlLCJkYXRhX3JldGVudGlvbiI6dHJ1ZSwibWVzc2FnZV9leHBvcnQiOnRydWUsImN1c3RvbV9wZXJtaXNzaW9uc19zY2hlbWVzIjp0cnVlLCJjdXN0b21fdGVybXNfb2Zfc2VydmljZSI6dHJ1ZSwiZ3Vlc3RfYWNjb3VudHMiOnRydWUsImd1ZXN0X2FjY291bnRzX3Blcm1pc3Npb25zIjp0cnVlLCJpZF9sb2FkZWQiOnRydWUsImxvY2tfdGVhbW1hdGVfbmFtZV9kaXNwbGF5Ijp0cnVlLCJjbG91ZCI6ZmFsc2UsInNoYXJlZF9jaGFubmVscyI6dHJ1ZSwicmVtb3RlX2NsdXN0ZXJfc2VydmljZSI6dHJ1ZSwib3BlbmlkIjp0cnVlLCJlbnRlcnByaXNlX3BsdWdpbnMiOnRydWUsImFkdmFuY2VkX2xvZ2dpbmciOnRydWUsImZ1dHVyZV9mZWF0dXJlcyI6dHJ1ZX0sImlzX3RyaWFsIjpmYWxzZSwiaXNfZ292X3NrdSI6ZmFsc2V9IMay/e4rVqZ1yEluKxCtWQJK8iWdpADuWyETHJcCDMV8ouQK3n/ocJsg1y7INrbSPZDw6quohjblLN5MqHLQi0c+5yRYwzBhisJD6MFWxFCSg99eSXqIeudAfKVU+WOdZxWhyLzob14hOEfjvN/2hNSNyTV4hqhzk62L9vHzzZsgrFu+zYu4pA6Y4yzZF9FyVvHW241BkGq7ZecmyS6NQsq1jlAhkoBpdW9PCvFDfYS3+CwKtWNfebItc4e9JTbVpo75n++59WV2faQDfiMBf2bYwe6OxzJIZ258r8C2KMFD1uqpQohIoDS9ziygAu2voqgsQqm1Btf1hMtgFAOW7w==", }, @@ -418,8 +415,6 @@ func RunContainer(ctx context.Context, opts ...MattermostCustomizeRequestOption) WaitingFor: wait.ForAll( wait.ForLog("Server is listening on"), ).WithDeadline(30 * time.Second), - Networks: []string{newNetwork.Name}, - NetworkAliases: map[string][]string{newNetwork.Name: {"mattermost"}}, }, Started: true, }, @@ -434,27 +429,40 @@ func RunContainer(ctx context.Context, opts ...MattermostCustomizeRequestOption) opt(&req) } + if req.network == nil { + newNetwork, err := network.New(ctx, network.WithCheckDuplicate()) + if err != nil { + return nil, err + } + req.network = newNetwork + } + + postgresContainer, err := runPostgresContainer(ctx, req.network) + if err != nil { + if err2 := req.network.Remove(ctx); err2 != nil { + err = fmt.Errorf("%w + %w", err, err2) + } + return nil, err + } + container, err := testcontainers.GenericContainer(ctx, req.GenericContainerRequest) if err != nil { if err2 := postgresContainer.Terminate(ctx); err2 != nil { err = fmt.Errorf("%w + %w", err, err2) } - if err2 := newNetwork.Remove(ctx); err2 != nil { + if err2 := req.network.Remove(ctx); err2 != nil { err = fmt.Errorf("%w + %w", err, err2) } return nil, err } - if req.logConsumer != nil { - container.FollowOutput(req.logConsumer) - } - mattermost := &MattermostContainer{ - Container: container, - pgContainer: postgresContainer, - network: newNetwork, - username: req.username, - password: req.password, + Container: container, + pgContainer: postgresContainer, + network: req.network, + customNetwork: req.customNetwork, + username: req.username, + password: req.password, } if err := mattermost.init(ctx, req); err != nil { diff --git a/server/testutils/testmodels/testmodels.go b/server/testutils/testmodels/testmodels.go deleted file mode 100644 index d5fd8979f..000000000 --- a/server/testutils/testmodels/testmodels.go +++ /dev/null @@ -1,7 +0,0 @@ -package testmodels - -type MockCallReturns struct { - Returns interface{} - ReturnType string - Err string -}