mirror of
https://github.com/ProtonMail/proton-bridge.git
synced 2025-12-10 12:46:46 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 61e4ca5814 | |||
| 8e0693ab03 | |||
| a3d2df9d38 | |||
| f9f4ce996d | |||
| fc69b9aabb |
@ -48,7 +48,7 @@ lint:
|
||||
tags:
|
||||
- medium
|
||||
|
||||
test:
|
||||
test-linux:
|
||||
stage: test
|
||||
only:
|
||||
- branches
|
||||
@ -65,6 +65,14 @@ test:
|
||||
tags:
|
||||
- medium
|
||||
|
||||
test-windows:
|
||||
extends: .build-windows-base
|
||||
stage: test
|
||||
only:
|
||||
- branches
|
||||
script:
|
||||
- make test
|
||||
|
||||
test-integration:
|
||||
stage: test
|
||||
only:
|
||||
@ -96,6 +104,7 @@ build-qml:
|
||||
- cd internal/frontend/qml
|
||||
- tar -cvzf ../../../bridge_qml.tgz ./*
|
||||
|
||||
|
||||
.build-base:
|
||||
stage: build
|
||||
only:
|
||||
@ -134,6 +143,7 @@ build-linux-qa:
|
||||
paths:
|
||||
- bridge_*.tgz
|
||||
|
||||
|
||||
.build-darwin-base:
|
||||
extends: .build-base
|
||||
before_script:
|
||||
@ -170,6 +180,40 @@ build-darwin-qa:
|
||||
paths:
|
||||
- bridge_*.tgz
|
||||
|
||||
|
||||
.build-windows-base:
|
||||
extends: .build-base
|
||||
before_script:
|
||||
- export GOROOT=/c/Go
|
||||
- export PATH=$GOROOT/bin:$PATH
|
||||
- export GOARCH=amd64
|
||||
- export GOPATH=~/go
|
||||
- export GO111MODULE=on
|
||||
- export PATH=$GOPATH/bin:$PATH
|
||||
- export MSYSTEM=
|
||||
- export PATH=$PATH:/c/grrrQt/5.13.2/mingw73_64/bin
|
||||
tags:
|
||||
- windows-bridge
|
||||
|
||||
build-windows:
|
||||
extends: .build-windows-base
|
||||
artifacts:
|
||||
name: "bridge-windows-$CI_COMMIT_SHORT_SHA"
|
||||
paths:
|
||||
- bridge_*.tgz
|
||||
|
||||
build-windows-qa:
|
||||
extends: .build-windows-base
|
||||
only:
|
||||
- web
|
||||
- branches
|
||||
script:
|
||||
- BUILD_TAGS="build_qa" make build
|
||||
artifacts:
|
||||
name: "bridge-windows-qa-$CI_COMMIT_SHORT_SHA"
|
||||
paths:
|
||||
- bridge_*.tgz
|
||||
|
||||
# Stage: MIRROR
|
||||
|
||||
mirror-repo:
|
||||
|
||||
11
Changelog.md
11
Changelog.md
@ -2,6 +2,17 @@
|
||||
|
||||
Changelog [format](http://keepachangelog.com/en/1.0.0/)
|
||||
|
||||
## [Bridge 2.1.0] London
|
||||
|
||||
## Added
|
||||
* GODT-1376: Add first userID to sentry scope.
|
||||
* GODT-1375: Add host architecture to sentry reports.
|
||||
* GODT-1364: Add windows CI machine for tests, and build.
|
||||
|
||||
### Fixed
|
||||
* GODT-1499: Remove message from DB once it is not on server any more.
|
||||
|
||||
|
||||
## [Bridge 2.1.0] London
|
||||
|
||||
### Fixed
|
||||
|
||||
2
Makefile
2
Makefile
@ -10,7 +10,7 @@ TARGET_OS?=${GOOS}
|
||||
.PHONY: build build-nogui build-launcher versioner hasher
|
||||
|
||||
# Keep version hardcoded so app build works also without Git repository.
|
||||
BRIDGE_APP_VERSION?=2.1.0+git
|
||||
BRIDGE_APP_VERSION?=2.1.1+git
|
||||
APP_VERSION:=${BRIDGE_APP_VERSION}
|
||||
SRC_ICO:=logo.ico
|
||||
SRC_ICNS:=Bridge.icns
|
||||
|
||||
6
go.mod
6
go.mod
@ -28,6 +28,8 @@ require (
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 // indirect
|
||||
github.com/cucumber/godog v0.12.1
|
||||
github.com/cucumber/messages-go/v16 v16.0.1
|
||||
github.com/elastic/go-sysinfo v1.7.1
|
||||
github.com/elastic/go-windows v1.0.1 // indirect
|
||||
github.com/emersion/go-imap-appendlimit v0.0.0-20190308131241-25671c986a6a
|
||||
github.com/emersion/go-imap-move v0.0.0-20190710073258-6e5a51a5b342
|
||||
github.com/emersion/go-imap-quota v0.0.0-20210203125329-619074823f3c
|
||||
@ -53,6 +55,7 @@ require (
|
||||
github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce
|
||||
github.com/olekukonko/tablewriter v0.0.4 // indirect
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/prometheus/procfs v0.7.3 // indirect
|
||||
github.com/ricochet2200/go-disk-usage/du v0.0.0-20210707232629-ac9918953285
|
||||
github.com/sirupsen/logrus v1.7.0
|
||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
||||
@ -64,8 +67,9 @@ require (
|
||||
github.com/vmihailenco/msgpack/v5 v5.1.3
|
||||
go.etcd.io/bbolt v1.3.6
|
||||
golang.org/x/net v0.0.0-20211008194852-3b03d305991f
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e
|
||||
golang.org/x/sys v0.0.0-20220111092808-5a964db01320
|
||||
golang.org/x/text v0.3.7
|
||||
howett.net/plist v1.0.0 // indirect
|
||||
)
|
||||
|
||||
replace (
|
||||
|
||||
21
go.sum
21
go.sum
@ -113,6 +113,11 @@ github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUn
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
|
||||
github.com/elastic/go-sysinfo v1.7.1 h1:Wx4DSARcKLllpKT2TnFVdSUJOsybqMYCNQZq1/wO+s0=
|
||||
github.com/elastic/go-sysinfo v1.7.1/go.mod h1:i1ZYdU10oLNfRzq4vq62BEwD2fH8KaWh6eh0ikPT9F0=
|
||||
github.com/elastic/go-windows v1.0.0/go.mod h1:TsU0Nrp7/y3+VwE82FoZF8gC/XFg/Elz6CcloAxnPgU=
|
||||
github.com/elastic/go-windows v1.0.1 h1:AlYZOldA+UJ0/2nBuqWdo90GFCgG9xuyw9SYzGUtJm0=
|
||||
github.com/elastic/go-windows v1.0.1/go.mod h1:FoVvqWSun28vaDQPbj2Elfc0JahhPB7WQEGa3c814Ss=
|
||||
github.com/emersion/go-imap-appendlimit v0.0.0-20190308131241-25671c986a6a h1:bMdSPm6sssuOFpIaveu3XGAijMS3Tq2S3EqFZmZxidc=
|
||||
github.com/emersion/go-imap-appendlimit v0.0.0-20190308131241-25671c986a6a/go.mod h1:ikgISoP7pRAolqsVP64yMteJa2FIpS6ju88eBT6K1yQ=
|
||||
github.com/emersion/go-imap-move v0.0.0-20190710073258-6e5a51a5b342 h1:5p1t3e1PomYgLWwEwhwEU5kVBwcyAcVrOpexv8AeZx0=
|
||||
@ -179,6 +184,7 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
@ -241,6 +247,9 @@ github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0Gqw
|
||||
github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw=
|
||||
github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7 h1:g0fAGBisHaEQ0TRq1iBvemFRf+8AEWEmBESSiWB3Vsc=
|
||||
github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 h1:rp+c0RAYOWj8l6qbCUTSiRLG/iKnW3K3/QfPPuSsBt4=
|
||||
github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
@ -347,7 +356,10 @@ github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190425082905-87a4384529e0/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
|
||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/ricochet2200/go-disk-usage/du v0.0.0-20210707232629-ac9918953285 h1:d54EL9l+XteliUfUCGsEwwuk65dmmxX85VXF+9T6+50=
|
||||
github.com/ricochet2200/go-disk-usage/du v0.0.0-20210707232629-ac9918953285/go.mod h1:fxIDly1xtudczrZeOOlfaUvd2OPb2qZAPuWdU2BsBTk=
|
||||
@ -509,6 +521,7 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@ -532,11 +545,13 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191025021431-6c3a3bfe00ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/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-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -547,6 +562,8 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220111092808-5a964db01320 h1:0jf+tOCoZ3LyutmCOWpVni1chK4VfFLhRsDK7MhqGRY=
|
||||
golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@ -622,6 +639,7 @@ gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
@ -635,4 +653,7 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=
|
||||
howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
|
||||
howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
|
||||
@ -189,6 +189,8 @@ func New( // nolint[funlen]
|
||||
|
||||
cm := pmapi.New(cfg)
|
||||
|
||||
sentryReporter.SetClientFromManager(cm)
|
||||
|
||||
cm.AddConnectionObserver(pmapi.NewConnectionObserver(
|
||||
func() { listener.Emit(events.InternetOffEvent, "") },
|
||||
func() { listener.Emit(events.InternetOnEvent, "") },
|
||||
|
||||
@ -25,81 +25,118 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const testPrefFilePath = "/tmp/pref.json"
|
||||
|
||||
func TestLoadNoKeyValueStore(t *testing.T) {
|
||||
pref := newTestEmptyKeyValueStore(t)
|
||||
require.Equal(t, "", pref.Get("key"))
|
||||
r := require.New(t)
|
||||
pref, clean := newTestEmptyKeyValueStore(r)
|
||||
defer clean()
|
||||
|
||||
r.Equal("", pref.Get("key"))
|
||||
}
|
||||
|
||||
func TestLoadBadKeyValueStore(t *testing.T) {
|
||||
require.NoError(t, ioutil.WriteFile(testPrefFilePath, []byte("{\"key\":\"value"), 0700))
|
||||
pref := newKeyValueStore(testPrefFilePath)
|
||||
require.Equal(t, "", pref.Get("key"))
|
||||
r := require.New(t)
|
||||
path, clean := newTmpFile(r)
|
||||
defer clean()
|
||||
|
||||
r.NoError(ioutil.WriteFile(path, []byte("{\"key\":\"MISSING_QUOTES"), 0700))
|
||||
pref := newKeyValueStore(path)
|
||||
r.Equal("", pref.Get("key"))
|
||||
}
|
||||
|
||||
func TestKeyValueStoreGet(t *testing.T) {
|
||||
pref := newTestKeyValueStore(t)
|
||||
require.Equal(t, "value", pref.Get("str"))
|
||||
require.Equal(t, "42", pref.Get("int"))
|
||||
require.Equal(t, "true", pref.Get("bool"))
|
||||
require.Equal(t, "t", pref.Get("falseBool"))
|
||||
func TestKeyValueStor(t *testing.T) {
|
||||
r := require.New(t)
|
||||
pref, clean := newTestKeyValueStore(r)
|
||||
defer clean()
|
||||
|
||||
r.Equal("value", pref.Get("str"))
|
||||
r.Equal("42", pref.Get("int"))
|
||||
r.Equal("true", pref.Get("bool"))
|
||||
r.Equal("t", pref.Get("falseBool"))
|
||||
}
|
||||
|
||||
func TestKeyValueStoreGetInt(t *testing.T) {
|
||||
pref := newTestKeyValueStore(t)
|
||||
require.Equal(t, 0, pref.GetInt("str"))
|
||||
require.Equal(t, 42, pref.GetInt("int"))
|
||||
require.Equal(t, 0, pref.GetInt("bool"))
|
||||
require.Equal(t, 0, pref.GetInt("falseBool"))
|
||||
r := require.New(t)
|
||||
pref, clean := newTestKeyValueStore(r)
|
||||
defer clean()
|
||||
|
||||
r.Equal(0, pref.GetInt("str"))
|
||||
r.Equal(42, pref.GetInt("int"))
|
||||
r.Equal(0, pref.GetInt("bool"))
|
||||
r.Equal(0, pref.GetInt("falseBool"))
|
||||
}
|
||||
|
||||
func TestKeyValueStoreGetBool(t *testing.T) {
|
||||
pref := newTestKeyValueStore(t)
|
||||
require.Equal(t, false, pref.GetBool("str"))
|
||||
require.Equal(t, false, pref.GetBool("int"))
|
||||
require.Equal(t, true, pref.GetBool("bool"))
|
||||
require.Equal(t, false, pref.GetBool("falseBool"))
|
||||
r := require.New(t)
|
||||
pref, clean := newTestKeyValueStore(r)
|
||||
defer clean()
|
||||
|
||||
r.Equal(false, pref.GetBool("str"))
|
||||
r.Equal(false, pref.GetBool("int"))
|
||||
r.Equal(true, pref.GetBool("bool"))
|
||||
r.Equal(false, pref.GetBool("falseBool"))
|
||||
}
|
||||
|
||||
func TestKeyValueStoreSetDefault(t *testing.T) {
|
||||
pref := newTestEmptyKeyValueStore(t)
|
||||
r := require.New(t)
|
||||
pref, clean := newTestEmptyKeyValueStore(r)
|
||||
defer clean()
|
||||
|
||||
pref.setDefault("key", "value")
|
||||
pref.setDefault("key", "othervalue")
|
||||
require.Equal(t, "value", pref.Get("key"))
|
||||
r.Equal("value", pref.Get("key"))
|
||||
}
|
||||
|
||||
func TestKeyValueStoreSet(t *testing.T) {
|
||||
pref := newTestEmptyKeyValueStore(t)
|
||||
r := require.New(t)
|
||||
pref, clean := newTestEmptyKeyValueStore(r)
|
||||
defer clean()
|
||||
|
||||
pref.Set("str", "value")
|
||||
checkSavedKeyValueStore(t, "{\n\t\"str\": \"value\"\n}")
|
||||
checkSavedKeyValueStore(r, pref.path, "{\n\t\"str\": \"value\"\n}")
|
||||
}
|
||||
|
||||
func TestKeyValueStoreSetInt(t *testing.T) {
|
||||
pref := newTestEmptyKeyValueStore(t)
|
||||
r := require.New(t)
|
||||
pref, clean := newTestEmptyKeyValueStore(r)
|
||||
defer clean()
|
||||
|
||||
pref.SetInt("int", 42)
|
||||
checkSavedKeyValueStore(t, "{\n\t\"int\": \"42\"\n}")
|
||||
checkSavedKeyValueStore(r, pref.path, "{\n\t\"int\": \"42\"\n}")
|
||||
}
|
||||
|
||||
func TestKeyValueStoreSetBool(t *testing.T) {
|
||||
pref := newTestEmptyKeyValueStore(t)
|
||||
r := require.New(t)
|
||||
pref, clean := newTestEmptyKeyValueStore(r)
|
||||
defer clean()
|
||||
|
||||
pref.SetBool("trueBool", true)
|
||||
pref.SetBool("falseBool", false)
|
||||
checkSavedKeyValueStore(t, "{\n\t\"falseBool\": \"false\",\n\t\"trueBool\": \"true\"\n}")
|
||||
checkSavedKeyValueStore(r, pref.path, "{\n\t\"falseBool\": \"false\",\n\t\"trueBool\": \"true\"\n}")
|
||||
}
|
||||
|
||||
func newTestEmptyKeyValueStore(t *testing.T) *keyValueStore {
|
||||
require.NoError(t, os.RemoveAll(testPrefFilePath))
|
||||
return newKeyValueStore(testPrefFilePath)
|
||||
func newTmpFile(r *require.Assertions) (path string, clean func()) {
|
||||
tmpfile, err := ioutil.TempFile("", "pref.*.json")
|
||||
r.NoError(err)
|
||||
defer r.NoError(tmpfile.Close())
|
||||
|
||||
return tmpfile.Name(), func() {
|
||||
r.NoError(os.Remove(tmpfile.Name()))
|
||||
}
|
||||
}
|
||||
|
||||
func newTestKeyValueStore(t *testing.T) *keyValueStore {
|
||||
require.NoError(t, ioutil.WriteFile(testPrefFilePath, []byte("{\"str\":\"value\",\"int\":\"42\",\"bool\":\"true\",\"falseBool\":\"t\"}"), 0700))
|
||||
return newKeyValueStore(testPrefFilePath)
|
||||
func newTestEmptyKeyValueStore(r *require.Assertions) (*keyValueStore, func()) {
|
||||
path, clean := newTmpFile(r)
|
||||
return newKeyValueStore(path), clean
|
||||
}
|
||||
|
||||
func checkSavedKeyValueStore(t *testing.T, expected string) {
|
||||
data, err := ioutil.ReadFile(testPrefFilePath)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expected, string(data))
|
||||
func newTestKeyValueStore(r *require.Assertions) (*keyValueStore, func()) {
|
||||
path, clean := newTmpFile(r)
|
||||
r.NoError(ioutil.WriteFile(path, []byte("{\"str\":\"value\",\"int\":\"42\",\"bool\":\"true\",\"falseBool\":\"t\"}"), 0700))
|
||||
return newKeyValueStore(path), clean
|
||||
}
|
||||
|
||||
func checkSavedKeyValueStore(r *require.Assertions, path, expected string) {
|
||||
data, err := ioutil.ReadFile(path)
|
||||
r.NoError(err)
|
||||
r.Equal(expected, string(data))
|
||||
}
|
||||
|
||||
49
internal/sentry/hostarch_darwin.go
Normal file
49
internal/sentry/hostarch_darwin.go
Normal file
@ -0,0 +1,49 @@
|
||||
// Copyright (c) 2022 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail Bridge is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ProtonMail Bridge is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package sentry
|
||||
|
||||
import (
|
||||
"github.com/elastic/go-sysinfo"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const translatedProcDarwin = "sysctl.proc_translated"
|
||||
|
||||
func getHostAarch() string {
|
||||
host, err := sysinfo.Host()
|
||||
if err != nil {
|
||||
return "not-detected"
|
||||
}
|
||||
|
||||
// It is not possible to retrieve real hardware architecture once using
|
||||
// rosetta. But it is possible to detect the process translation if
|
||||
// rosetta is used.
|
||||
res, err := unix.SysctlRaw(translatedProcDarwin)
|
||||
if err != nil || len(res) > 4 {
|
||||
return host.Info().Architecture + "_err"
|
||||
}
|
||||
|
||||
if res[0] == 1 {
|
||||
return host.Info().Architecture + "_rosetta"
|
||||
}
|
||||
|
||||
return host.Info().Architecture
|
||||
}
|
||||
31
internal/sentry/hostarch_default.go
Normal file
31
internal/sentry/hostarch_default.go
Normal file
@ -0,0 +1,31 @@
|
||||
// Copyright (c) 2022 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail Bridge is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ProtonMail Bridge is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//go:build !darwin
|
||||
// +build !darwin
|
||||
|
||||
package sentry
|
||||
|
||||
import "github.com/elastic/go-sysinfo"
|
||||
|
||||
func getHostAarch() string {
|
||||
host, err := sysinfo.Host()
|
||||
if err != nil {
|
||||
return "not-detected"
|
||||
}
|
||||
return host.Info().Architecture
|
||||
}
|
||||
@ -20,18 +20,20 @@ package sentry
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/ProtonMail/proton-bridge/internal/constants"
|
||||
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
||||
"github.com/getsentry/sentry-go"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var skippedFunctions = []string{} //nolint[gochecknoglobals]
|
||||
|
||||
func init() { // nolint[noinit]
|
||||
func init() { //nolint[noinit, gochecknoinits]
|
||||
if err := sentry.Init(sentry.ClientOptions{
|
||||
Dsn: constants.DSNSentry,
|
||||
Release: constants.Revision,
|
||||
@ -42,13 +44,20 @@ func init() { // nolint[noinit]
|
||||
|
||||
sentry.ConfigureScope(func(scope *sentry.Scope) {
|
||||
scope.SetFingerprint([]string{"{{ default }}"})
|
||||
scope.SetTag("UserID", "not-defined")
|
||||
})
|
||||
|
||||
sentry.Logger = log.New(
|
||||
logrus.WithField("pkg", "sentry-go").WriterLevel(logrus.WarnLevel),
|
||||
"", 0,
|
||||
)
|
||||
}
|
||||
|
||||
type Reporter struct {
|
||||
appName string
|
||||
appVersion string
|
||||
userAgent fmt.Stringer
|
||||
hostArch string
|
||||
}
|
||||
|
||||
// NewReporter creates new sentry reporter with appName and appVersion to report.
|
||||
@ -57,6 +66,7 @@ func NewReporter(appName, appVersion string, userAgent fmt.Stringer) *Reporter {
|
||||
appName: appName,
|
||||
appVersion: appVersion,
|
||||
userAgent: userAgent,
|
||||
hostArch: getHostAarch(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,7 +119,7 @@ func (r *Reporter) scopedReport(context map[string]interface{}, doReport func())
|
||||
"Client": r.appName,
|
||||
"Version": r.appVersion,
|
||||
"UserAgent": r.userAgent.String(),
|
||||
"UserID": "",
|
||||
"HostArch": r.hostArch,
|
||||
}
|
||||
|
||||
sentry.WithScope(func(scope *sentry.Scope) {
|
||||
@ -179,3 +189,6 @@ func isFunctionFilteredOut(function string) bool {
|
||||
func Flush(maxWaiTime time.Duration) {
|
||||
sentry.Flush(maxWaiTime)
|
||||
}
|
||||
|
||||
func (r *Reporter) SetClientFromManager(cm pmapi.Manager) {
|
||||
}
|
||||
|
||||
@ -23,6 +23,7 @@ import (
|
||||
"github.com/ProtonMail/gopenpgp/v2/crypto"
|
||||
"github.com/ProtonMail/proton-bridge/internal/store/cache"
|
||||
"github.com/ProtonMail/proton-bridge/pkg/message"
|
||||
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
@ -126,6 +127,7 @@ func (store *Store) getCachedMessage(messageID string) ([]byte, error) {
|
||||
|
||||
literal, err := job.GetResult()
|
||||
if err != nil {
|
||||
store.checkAndRemoveDeletedMessage(err, messageID)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -184,8 +186,21 @@ func (store *Store) BuildAndCacheMessage(ctx context.Context, messageID string)
|
||||
|
||||
literal, err := job.GetResult()
|
||||
if err != nil {
|
||||
store.checkAndRemoveDeletedMessage(err, messageID)
|
||||
return err
|
||||
}
|
||||
|
||||
return store.cache.Set(store.user.ID(), messageID, literal)
|
||||
}
|
||||
|
||||
func (store *Store) checkAndRemoveDeletedMessage(err error, msgID string) {
|
||||
if _, ok := err.(pmapi.ErrUnprocessableEntity); !ok {
|
||||
return
|
||||
}
|
||||
l := store.log.WithError(err).WithField("msgID", msgID)
|
||||
l.Warn("Deleting message which was not found on API")
|
||||
|
||||
if deleteErr := store.deleteMessageEvent(msgID); deleteErr != nil {
|
||||
l.WithField("deleteErr", deleteErr).Error("Failed to delete non-existed API message from DB")
|
||||
}
|
||||
}
|
||||
|
||||
8
internal/store/cache/disk.go
vendored
8
internal/store/cache/disk.go
vendored
@ -63,10 +63,16 @@ func NewOnDiskCache(path string, cmp Compressor, opts Options) (Cache, error) {
|
||||
}
|
||||
|
||||
file, err := ioutil.TempFile(path, "tmp")
|
||||
defer func() {
|
||||
file.Close() //nolint[errcheck]
|
||||
os.Remove(file.Name()) //nolint[errcheck]
|
||||
}()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot open test write target: %w", err)
|
||||
}
|
||||
if _, err := file.Write([]byte("test-write")); err != nil {
|
||||
return nil, fmt.Errorf("cannot write to target: %w", err)
|
||||
}
|
||||
os.Remove(file.Name()) //nolint[errcheck]
|
||||
|
||||
usage := du.NewDiskUsage(path)
|
||||
|
||||
|
||||
@ -229,7 +229,10 @@ func (u *Users) MigrateCache(srcPath, dstPath string) error {
|
||||
// (read-only is conserved). Do copy instead.
|
||||
tmp, err := ioutil.TempFile(srcPath, "tmp")
|
||||
if err == nil {
|
||||
defer os.Remove(tmp.Name()) //nolint[errcheck]
|
||||
defer func() {
|
||||
tmp.Close() //nolint[errcheck]
|
||||
os.Remove(tmp.Name()) //nolint[errcheck]
|
||||
}()
|
||||
|
||||
if err := os.Rename(srcPath, dstPath); err == nil {
|
||||
return nil
|
||||
|
||||
@ -175,6 +175,7 @@ func initMocks(t *testing.T) mocks {
|
||||
|
||||
cacheFile, err := ioutil.TempFile("", "bridge-store-cache-*.db")
|
||||
r.NoError(t, err, "could not get temporary file for store cache")
|
||||
r.NoError(t, cacheFile.Close())
|
||||
|
||||
m := mocks{
|
||||
t: t,
|
||||
@ -201,6 +202,7 @@ func initMocks(t *testing.T) mocks {
|
||||
|
||||
dbFile, err := ioutil.TempFile(t.TempDir(), "bridge-store-db-*.db")
|
||||
r.NoError(t, err, "could not get temporary file for store db")
|
||||
r.NoError(t, dbFile.Close())
|
||||
|
||||
return store.New(
|
||||
sentryReporter,
|
||||
|
||||
@ -32,7 +32,7 @@ import (
|
||||
// RemoveOldVersions is a noop on darwin; we don't test it there.
|
||||
|
||||
func TestRemoveOldVersions(t *testing.T) {
|
||||
updates, err := ioutil.TempDir("", "updates")
|
||||
updates, err := ioutil.TempDir(t.TempDir(), "updates")
|
||||
require.NoError(t, err)
|
||||
|
||||
v := newTestVersioner(t, "myCoolApp", updates, "2.3.4-beta", "2.3.4", "2.3.5", "2.4.0")
|
||||
|
||||
@ -65,11 +65,13 @@ func makeDummyVersionDirectory(t *testing.T, exeName, updates, version string) s
|
||||
exe, err := os.Create(filepath.Join(target, getExeName(exeName)))
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, exe)
|
||||
require.NoError(t, exe.Close())
|
||||
require.NoError(t, os.Chmod(exe.Name(), 0700))
|
||||
|
||||
sig, err := os.Create(filepath.Join(target, getExeName(exeName)+".sig"))
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, sig)
|
||||
require.NoError(t, sig.Close())
|
||||
|
||||
return target
|
||||
}
|
||||
|
||||
@ -21,6 +21,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -33,6 +34,7 @@ var (
|
||||
wantOutput = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
||||
testProcessSleep = 100 // ms
|
||||
runParallelTimeOverhead = 150 // ms
|
||||
windowsCIExtra = 250 // ms - estimated experimentally
|
||||
)
|
||||
|
||||
func TestParallel(t *testing.T) {
|
||||
@ -56,6 +58,9 @@ func TestParallel(t *testing.T) {
|
||||
|
||||
wantMinDuration := int(math.Ceil(float64(len(testInput))/float64(workers))) * testProcessSleep
|
||||
wantMaxDuration := wantMinDuration + runParallelTimeOverhead
|
||||
if runtime.GOOS == "windows" {
|
||||
wantMaxDuration += windowsCIExtra
|
||||
}
|
||||
r.True(t, duration.Nanoseconds() > int64(wantMinDuration*1000000), "Duration too short: %v (expected: %v)", duration, wantMinDuration)
|
||||
r.True(t, duration.Nanoseconds() < int64(wantMaxDuration*1000000), "Duration too long: %v (expected: %v)", duration, wantMaxDuration)
|
||||
})
|
||||
|
||||
@ -82,4 +82,5 @@ type AuthRefreshHandler func(*AuthRefresh)
|
||||
type clientManager interface {
|
||||
r(context.Context) *resty.Request
|
||||
authRefresh(context.Context, string, string) (*AuthRefresh, error)
|
||||
setSentryUserID(userID string)
|
||||
}
|
||||
|
||||
@ -32,9 +32,9 @@ var (
|
||||
)
|
||||
|
||||
type ErrUnprocessableEntity struct {
|
||||
originalError error
|
||||
OriginalError error
|
||||
}
|
||||
|
||||
func (err ErrUnprocessableEntity) Error() string {
|
||||
return err.originalError.Error()
|
||||
return err.OriginalError.Error()
|
||||
}
|
||||
|
||||
@ -23,6 +23,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/getsentry/sentry-go"
|
||||
"github.com/go-resty/resty/v2"
|
||||
)
|
||||
|
||||
@ -35,8 +36,9 @@ type manager struct {
|
||||
connectionObservers []ConnectionObserver
|
||||
proxyDialer *ProxyTLSDialer
|
||||
|
||||
pingMutex *sync.RWMutex
|
||||
isPinging bool
|
||||
pingMutex *sync.RWMutex
|
||||
isPinging bool
|
||||
setSentryUserIDOnce sync.Once
|
||||
}
|
||||
|
||||
func New(cfg Config) Manager {
|
||||
@ -45,11 +47,12 @@ func New(cfg Config) Manager {
|
||||
|
||||
func newManager(cfg Config) *manager {
|
||||
m := &manager{
|
||||
cfg: cfg,
|
||||
rc: resty.New().EnableTrace(),
|
||||
locker: &sync.Mutex{},
|
||||
pingMutex: &sync.RWMutex{},
|
||||
isPinging: false,
|
||||
cfg: cfg,
|
||||
rc: resty.New().EnableTrace(),
|
||||
locker: &sync.Mutex{},
|
||||
pingMutex: &sync.RWMutex{},
|
||||
isPinging: false,
|
||||
setSentryUserIDOnce: sync.Once{},
|
||||
}
|
||||
|
||||
proxyDialer, transport := newProxyDialerAndTransport(cfg)
|
||||
@ -158,3 +161,11 @@ func (m *manager) handleRequestFailure(req *resty.Request, err error) {
|
||||
|
||||
go m.pingUntilSuccess()
|
||||
}
|
||||
|
||||
func (m *manager) setSentryUserID(userID string) {
|
||||
m.setSentryUserIDOnce.Do(func() {
|
||||
sentry.ConfigureScope(func(scope *sentry.Scope) {
|
||||
scope.SetTag("UserID", userID)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@ -21,7 +21,6 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/ProtonMail/gopenpgp/v2/crypto"
|
||||
"github.com/getsentry/sentry-go"
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@ -126,7 +125,7 @@ func (c *client) UpdateUser(ctx context.Context) (*User, error) {
|
||||
|
||||
c.user = user
|
||||
c.addresses = addresses
|
||||
sentry.ConfigureScope(func(scope *sentry.Scope) { scope.SetUser(sentry.User{ID: user.ID}) })
|
||||
c.manager.setSentryUserID(user.ID)
|
||||
|
||||
return user, err
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
.PHONY: check-go check-godog install-godog test test-bridge test-live test-live-bridge test-stage test-debug test-live-debug bench
|
||||
|
||||
export GO111MODULE=on
|
||||
export BRIDGE_VERSION:=2.1.0+integrationtests
|
||||
export BRIDGE_VERSION:=2.1.1+integrationtests
|
||||
export VERBOSITY?=fatal
|
||||
export TEST_DATA=testdata
|
||||
|
||||
|
||||
@ -45,6 +45,7 @@ type PMAPIController interface {
|
||||
GetCalls(method, path string) [][]byte
|
||||
LockEvents(username string)
|
||||
UnlockEvents(username string)
|
||||
RemoveUserMessageWithoutEvent(username, messageID string) error
|
||||
}
|
||||
|
||||
func newPMAPIController(listener listener.Listener) (PMAPIController, pmapi.Manager) {
|
||||
|
||||
@ -234,3 +234,19 @@ func (ctl *Controller) LockEvents(string) {}
|
||||
|
||||
// UnlockEvents doesn't needs to be implemented for fakeAPI.
|
||||
func (ctl *Controller) UnlockEvents(string) {}
|
||||
|
||||
func (ctl *Controller) RemoveUserMessageWithoutEvent(username string, messageID string) error {
|
||||
msgs, ok := ctl.messagesByUsername[username]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
for i, message := range msgs {
|
||||
if message.ID == messageID {
|
||||
ctl.messagesByUsername[username] = append(msgs[:i], msgs[i+1:]...)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return errors.New("message not found")
|
||||
}
|
||||
|
||||
@ -37,7 +37,7 @@ func (api *FakePMAPI) GetMessage(_ context.Context, apiID string) (*pmapi.Messag
|
||||
if msg := api.getMessage(apiID); msg != nil {
|
||||
return msg, nil
|
||||
}
|
||||
return nil, fmt.Errorf("message %s not found", apiID)
|
||||
return nil, pmapi.ErrUnprocessableEntity{OriginalError: fmt.Errorf("message %s not found", apiID)}
|
||||
}
|
||||
|
||||
// ListMessages does not implement following filters:
|
||||
|
||||
@ -177,3 +177,14 @@ Feature: IMAP fetch messages
|
||||
# We had bug to incorectly set empty date, so let's make sure
|
||||
# there is no reference anywhere in the response.
|
||||
And IMAP response does not contain "\nDate: Thu, 01 Jan 1970"
|
||||
|
||||
Scenario: Fetch of message which was deleted without event processed
|
||||
Given there are 10 messages in mailbox "INBOX" for "user"
|
||||
And message "5" was deleted forever without event processed for "user"
|
||||
And there is IMAP client logged in as "user"
|
||||
And there is IMAP client selected in "INBOX"
|
||||
When IMAP client fetches bodies "1:*"
|
||||
Then IMAP response is "NO"
|
||||
When IMAP client fetches bodies "1:*"
|
||||
Then IMAP response is "OK"
|
||||
And IMAP response has 9 messages
|
||||
|
||||
@ -104,3 +104,14 @@ func (ctl *Controller) GetMessages(username, labelID string) ([]*pmapi.Message,
|
||||
|
||||
return messages, nil
|
||||
}
|
||||
|
||||
func (ctl *Controller) RemoveUserMessageWithoutEvent(username string, messageID string) error {
|
||||
client, err := getPersistentClient(username)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
addMessageIDToSkipEventOnceDeleted(messageID)
|
||||
|
||||
return client.DeleteMessages(context.Background(), []string{messageID})
|
||||
}
|
||||
|
||||
@ -48,7 +48,8 @@ var persistentClients = struct {
|
||||
byName map[string]clientAuthGetter
|
||||
saltByName map[string]string
|
||||
|
||||
eventsPaused sync.WaitGroup
|
||||
eventsPaused sync.WaitGroup
|
||||
skipDeletedMessageID map[string]struct{}
|
||||
}{}
|
||||
|
||||
type persistentClient struct {
|
||||
@ -79,7 +80,40 @@ func (pc *persistentClient) GetEvent(ctx context.Context, eventID string) (*pmap
|
||||
if !ok {
|
||||
return nil, errors.New("cannot convert to normal client")
|
||||
}
|
||||
return normalClient.GetEvent(ctx, eventID)
|
||||
|
||||
event, err := normalClient.GetEvent(ctx, eventID)
|
||||
if err != nil {
|
||||
return event, err
|
||||
}
|
||||
|
||||
return skipDeletedMessageIDs(event), nil
|
||||
}
|
||||
|
||||
func addMessageIDToSkipEventOnceDeleted(msgID string) {
|
||||
if persistentClients.skipDeletedMessageID == nil {
|
||||
persistentClients.skipDeletedMessageID = map[string]struct{}{}
|
||||
}
|
||||
persistentClients.skipDeletedMessageID[msgID] = struct{}{}
|
||||
}
|
||||
|
||||
func skipDeletedMessageIDs(event *pmapi.Event) *pmapi.Event {
|
||||
if len(event.Messages) == 0 {
|
||||
return event
|
||||
}
|
||||
|
||||
n := 0
|
||||
for i, m := range event.Messages {
|
||||
if _, ok := persistentClients.skipDeletedMessageID[m.ID]; ok && m.Action == pmapi.EventDelete {
|
||||
delete(persistentClients.skipDeletedMessageID, m.ID)
|
||||
continue
|
||||
}
|
||||
|
||||
event.Messages[i] = m
|
||||
n++
|
||||
}
|
||||
event.Messages = event.Messages[:n]
|
||||
|
||||
return event
|
||||
}
|
||||
|
||||
func SetupPersistentClients() {
|
||||
|
||||
@ -38,6 +38,7 @@ func StoreSetupFeatureContext(s *godog.ScenarioContext) {
|
||||
s.Step(`^there are messages for "([^"]*)" as follows$`, thereAreSomeMessagesForUserAsFollows)
|
||||
s.Step(`^there are (\d+) messages in mailbox(?:es)? "([^"]*)" for address "([^"]*)" of "([^"]*)"$`, thereAreSomeMessagesInMailboxesForAddressOfUser)
|
||||
s.Step(`^wait for Sphinx to create duplication indices$`, waitForSphinx)
|
||||
s.Step(`^message(?:s)? "([^"]*)" (?:was|were) deleted forever without event processed for "([^"]*)"$`, messageWasDeletedWithoutEvent)
|
||||
}
|
||||
|
||||
func thereIsUserWithMailboxes(bddUserID string, mailboxes *godog.Table) error {
|
||||
@ -319,3 +320,16 @@ func waitForSphinx() error {
|
||||
time.Sleep(15 * time.Second)
|
||||
return nil
|
||||
}
|
||||
|
||||
func messageWasDeletedWithoutEvent(bddMessageID, bddUserID string) error {
|
||||
account := ctx.GetTestAccount(bddUserID)
|
||||
if account == nil {
|
||||
return godog.ErrPending
|
||||
}
|
||||
apiID, err := ctx.GetAPIMessageID(account.Username(), bddMessageID)
|
||||
if err != nil {
|
||||
return internalError(err, "getting BDD message ID %s", bddMessageID)
|
||||
}
|
||||
|
||||
return ctx.GetPMAPIController().RemoveUserMessageWithoutEvent(account.Username(), apiID)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user