Merge pull request #21 from appleboy/test

Fix #20 Testing
This commit is contained in:
Bo-Yi Wu 2016-04-02 01:05:29 +08:00
commit 17b9dc6ae8
12 changed files with 655 additions and 104 deletions

2
.gitignore vendored
View File

@ -28,6 +28,4 @@ key.pem
config.yaml config.yaml
bin/* bin/*
.DS_Store .DS_Store
*.cert
*.key
coverage.out coverage.out

View File

@ -11,7 +11,9 @@ go:
env: env:
global: global:
secure: ad5iOvyzCMdxp664YZD92Qse8EAB8R8jgoV8CspLBVbwfqPv2S+2IF2pHrxqd6o81y2sTgAr0VFOx9q9gVD3JZpT0vjpPIzscGNVYtup8bDBDFYIf3vwt6RVvJsSTYGQIQ6HulZ97CKB5W2pCI2q9pOrZqFzQG9lHA4J4m+XzAzH7eNx4PprboEwKKhESI4191ii47phWKcalcul00wo/oeM1u/Bmmg/rcGyQgzUHg3zFh+1avQp7z4yAMFmScRnJDJFABg9s9TVeQu8FQXUWTBv4tUQJrpPs60zRSxpt3ap9Yx67pkuVYpfZz9o9u6FLa8UC+zWVh+oHYXfYWrSmCcsyfLjy5Wg2Zt3Mx92Aqq8uiaGaLSxcn5pL9xeIK6X0uWixp2wzXMdjE9RkGRaPhTm/Ze4F810JyNR+i0AkM8py/gPEkVXkeIw+UGS/mo5bq4a5ntLKRbl1vePOIPUtpV8nlgBblNB6jqd3/wINRcCoNhNiKtr+uvUm1jKKK+hu4q9YncwG3gpB+u0DRR2KIPSgdUlzL3V43UMWIC12eEjVrS+xkMo7a2gfUXMzwmpMhQHZ2QMb0VMOgIde1X4nlOL0GHsrHJAp8AmjQF6xbv2gcxzs98zU9qKp/qm4QETGQvHaMXhvE8ogzx1EgXaJ0xn9SIAw11TBT6dpHLdcGI= - secure: ad5iOvyzCMdxp664YZD92Qse8EAB8R8jgoV8CspLBVbwfqPv2S+2IF2pHrxqd6o81y2sTgAr0VFOx9q9gVD3JZpT0vjpPIzscGNVYtup8bDBDFYIf3vwt6RVvJsSTYGQIQ6HulZ97CKB5W2pCI2q9pOrZqFzQG9lHA4J4m+XzAzH7eNx4PprboEwKKhESI4191ii47phWKcalcul00wo/oeM1u/Bmmg/rcGyQgzUHg3zFh+1avQp7z4yAMFmScRnJDJFABg9s9TVeQu8FQXUWTBv4tUQJrpPs60zRSxpt3ap9Yx67pkuVYpfZz9o9u6FLa8UC+zWVh+oHYXfYWrSmCcsyfLjy5Wg2Zt3Mx92Aqq8uiaGaLSxcn5pL9xeIK6X0uWixp2wzXMdjE9RkGRaPhTm/Ze4F810JyNR+i0AkM8py/gPEkVXkeIw+UGS/mo5bq4a5ntLKRbl1vePOIPUtpV8nlgBblNB6jqd3/wINRcCoNhNiKtr+uvUm1jKKK+hu4q9YncwG3gpB+u0DRR2KIPSgdUlzL3V43UMWIC12eEjVrS+xkMo7a2gfUXMzwmpMhQHZ2QMb0VMOgIde1X4nlOL0GHsrHJAp8AmjQF6xbv2gcxzs98zU9qKp/qm4QETGQvHaMXhvE8ogzx1EgXaJ0xn9SIAw11TBT6dpHLdcGI=
- secure: nnOFadWZodcsJeRHwcPE7wjUk4xZ7oNjrJUPvs1yBDKGOCfga/KbmWpQ85PZiJmmxA7WO1PXpRV0lf+lHKDuAJKiXbQZNZOW8BoFSagRy7/lSicDqeQyJX2zJsjYbM11ZqOCH1uiTMBsNsbaGJTGtwHrbQ84zXJk2WXvDfrvNqlo85cXDHtxkdsqfETgY3S7Gis7VUKmoJKHuhF8ADvsLjaXpg/kgnrhEkTz9AD4oxscMUq3/vkf5wPN9a4uEXDJamKK6N2CKfwGZLwVPGMRIrIL+/gc1vyALGpl5xjC/rMuErESVfzKcUfLgENXL4JEv2IcqfInDqhVL56YG87xlYb4Wsxsm0u1IYDl+v8Frm35SPAz7C5Iha+/Kh1G1ip9AZKkpSS7IxpFOfRe6hmhvXIX00cNCo51ZMp7xBkahiktHveXj7UrqX72y949/+QXgbC0AvV2+4lYnEDydkphlkXH9MDeAVtkbYGUss06U0q19GuL0O8+iZPyBfajdAdS0V0nx7zIYYW42UVuJdRcndcEkaK7PQd0fZlwwZUSXQgqEg8zd+VzxC7tAgtS8LlOU2Ma3Vf4pZiScaZNqpJXPIyD/jPSqNSYQe6WJ6FKRPJw+1ZwN2kAeQMIt1th+08I+23dyolpbULPTkOFudsZoyFkSPhEcDBKNEQdrL7ggTw=
- secure: MVVH7ilA3r3eLb1I9/KTn3hWaEWOhY8yeF1g2zO09r0oRwEr+NKba/Ey2TpBmkcbFt+CiKMrefzoG9dwHyHm9dFH/1OLBrRAXK3RLV6qyMNw708yOkc7NfH5xK7X7F2u0vWwagK3aVkhTxXGrIQvaEK5jJ2tK3K1uDO5TzUC4TM0hLsgGvyTah89LJBhM4k0OEcAIVmzPO58Ql+RZV3nw03LDtcLofVFNqApCAUJNPrArt9TP/UraPPg/R8WtAS/PMY6IsMRKWj3LjN/J089zfQgiHH6p2wPBQ0n2R2zgisnxjAz5wt6/Dkvo09UqkVWFGX4p6N4t7kxAZoqhRRt+t38qdDip5iloclHGO5eI3/dr30V88Y5ionLL81WsBRqFuMmWrEb86maSMsXWl5yM1qB76Rsh/sPMDRk99Wf9RqhHedJxek6reoUcTBZl9kgXJDlqpjEogyq6qgL0jCCqgl0N6FZAYE/5SvW82MCukZZmko2UXBS/GmU6fhBRGquzOczL43YvVqqV8olZ2qXIdeeX5KeO/SHMUvW3oUnENa6V49K6ZX1f7KG8p8jERTNuDfsdgvZdMSYR2EeDQ7RTU6fhoc3BX4cFyTrS1qImswoBvLCEr54awQXDhgWddZsIWQzCxw6TmD7u25G3YHTsM5Wvs2Gj4z0aJeXFlLSfSQ=
install: install:
- go get -t -v ./... - go get -t -v ./...

Binary file not shown.

View File

@ -0,0 +1,59 @@
Bag Attributes
localKeyID: 8C 1A 9F 00 66 BD 24 42 B9 5D 1E EB FE 5E 8B CA 04 3D 73 83
friendlyName: APNS/2 Private Key
subject=/C=NZ/ST=Wellington/L=Wellington/O=Internet Widgits Pty Ltd/OU=9ZEH62KRVV/CN=APNS/2 Development IOS Push Services: com.sideshow.Apns2
issuer=/C=NZ/ST=Wellington/L=Wellington/O=APNS/2 Inc./OU=APNS/2 Worldwide Developer Relations/CN=APNS/2 Worldwide Developer Relations Certification Authority
-----BEGIN CERTIFICATE-----
MIID6zCCAtMCAQIwDQYJKoZIhvcNAQELBQAwgcMxCzAJBgNVBAYTAk5aMRMwEQYD
VQQIEwpXZWxsaW5ndG9uMRMwEQYDVQQHEwpXZWxsaW5ndG9uMRQwEgYDVQQKEwtB
UE5TLzIgSW5jLjEtMCsGA1UECxMkQVBOUy8yIFdvcmxkd2lkZSBEZXZlbG9wZXIg
UmVsYXRpb25zMUUwQwYDVQQDEzxBUE5TLzIgV29ybGR3aWRlIERldmVsb3BlciBS
ZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTYwMTA4MDgzNDMw
WhcNMjYwMTA1MDgzNDMwWjCBsjELMAkGA1UEBhMCTloxEzARBgNVBAgTCldlbGxp
bmd0b24xEzARBgNVBAcTCldlbGxpbmd0b24xITAfBgNVBAoTGEludGVybmV0IFdp
ZGdpdHMgUHR5IEx0ZDETMBEGA1UECxMKOVpFSDYyS1JWVjFBMD8GA1UEAxM4QVBO
Uy8yIERldmVsb3BtZW50IElPUyBQdXNoIFNlcnZpY2VzOiBjb20uc2lkZXNob3cu
QXBuczIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDY0c1TKB5oZPwQ
7t1CwMIrvqB6GIU3tPy6RhckZXTkOB8YeBWJ7UKfCz8HGHFVomBP0T5OUbeqQzqW
YJbQzZ8a6ZMszbL0lO4X9++3Oi5/TtAwOUOK8rOFN25m2KfsayHQZ/4vWStK2Fwm
5aJbGLlpH/b/7z1D4vhmMgoBuT1IuyhGiyFxlZ9EtTloFvsqM1E5fYZOSZACyXTa
K4vdgbQMgUVsI714FAgLTlK0UeiRkmKm3pdbtfVbrthzI+IHXKItUIy+Fn20PRMh
dSnaztSz7tgBWCIx22qvcYogHWiOgUYIM772zE2y8UVOr8DsiRlsOHSA7EI4MJcQ
G2FUq2Z/AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGyfyO2HMgcdeBcz3bt5BILX
f7RA2/UmVIwcKR1qotTsF+PnBmcILeyOQgDe9tGU5cRc79kDt3JRmMYROFIMgFRf
Wf22uOKtho7GQQaKvG+bkgMVdYFRlBHnF+KeqKH81qb9p+CT4Iw0GehIL1DijFLR
VIAIBYpz4oBPCIE1ISVT+Fgaf3JAh59kbPbNw9AIDxaBtP8EuzSTNwfbxoGbCobS
Wi1U8IsCwQFt8tM1m4ZXD1CcZIrGdryeAhVkvKIJRiU5QYWI2nqZN+JqQucm9ad0
mYO5mJkIobUa4+ZJhCPKEdmgpFbRGk0wVuaDM9Cv6P2srsYAjaO4y3VP0GvNKRI=
-----END CERTIFICATE-----
Bag Attributes
localKeyID: 8C 1A 9F 00 66 BD 24 42 B9 5D 1E EB FE 5E 8B CA 04 3D 73 83
friendlyName: APNS/2 Private Key
Key Attributes: <No Attributes>
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA2NHNUygeaGT8EO7dQsDCK76gehiFN7T8ukYXJGV05DgfGHgV
ie1Cnws/BxhxVaJgT9E+TlG3qkM6lmCW0M2fGumTLM2y9JTuF/fvtzouf07QMDlD
ivKzhTduZtin7Gsh0Gf+L1krSthcJuWiWxi5aR/2/+89Q+L4ZjIKAbk9SLsoRosh
cZWfRLU5aBb7KjNROX2GTkmQAsl02iuL3YG0DIFFbCO9eBQIC05StFHokZJipt6X
W7X1W67YcyPiB1yiLVCMvhZ9tD0TIXUp2s7Us+7YAVgiMdtqr3GKIB1ojoFGCDO+
9sxNsvFFTq/A7IkZbDh0gOxCODCXEBthVKtmfwIDAQABAoIBAQCW8ZCI+OAae1tE
ipZ9F2bWP3LHLXTo8FYVdCA+VWeITk3PoiIUkJmV0aWCUhDstgto5doDej5sCTur
Xvj/ynaerMeqJFYWkewjwZcgLyAZvwuO1v7fp9E0x/9TGDfnjjnPNeaundxW0cNt
zOY3l0HVHsy9Jpe3QDcAJovy4Tv5+hFY4kDxUBGsyjvhScVgKg5tLkJclm3sOu/L
GyLqpwNI3OJAdMIuVD4N2BZ1aOEap6mp2y8Ie0/R4YWcaZ5A4Pw7xUPl6SXc9uua
/78QTERtPC6ejyCBiE05a8m3Q3iud3Xtnlyws2KwhgBAfE6M4zR/f3OQB7ZIXMhy
ZpmZZw5xAoGBAPYn84IrlIQetWQfvPdM7Kzgh6UDHCugnlCDghwYpRJGi8hMfuZV
xNIrYAJzLYDQ01lFJRJgWXTcbqz9NBz1nhg+cNOz1/KY+38eudee6DNYmztP7jDP
2jnaS+dtjC8hAXObnFqG+NilMDLLu6aRmrJaImbjSrfyLiE6mvJ7u81nAoGBAOF9
g93wZ0mL1rk2s5WwHGTNU/HaOtmWS4z7kA7f4QaRub+MwppZmmDZPHpiZX7BPcZz
iOPQh+xn7IqRGoQWBLykBVt8zZFoLZJoCR3n63lex5A4p/0Pp1gFZrR+xX8PYVos
3yeeiWyPKsXXNc0s5QwHZcX6Wb8EHThTXGCBetcpAoGAMeQJC9IPaPPcae2w3CLA
OY3MkFpgBEuqqsDsxwsLsfeQb0lp0v+BQ+O8suJrT5eDrq1ABUh3+SKQYAl13YS+
xUUqkw35b9cn6iztF9HCWF3WIKBjs4r9PQqMpdxjNE4pQChC+Wov16ErcrAuWWVb
iFiSbm4U/9FbHisFqq3/c3MCgYB+vzSuPgFw37+0oEDVtQZgyuGSop5NzCNvfb/9
/G3aaXNFbnO8mv0hzzoleMWgODLnJ+4cUAz3H3tgcCu9bzr+Zhv0zvQl9a8YCo6F
VuWPdW0rbg1PO8tOuMqATnno79ZC/9H3zS9l7BuY1V2SlNeyqT3VyOFFc6SREpps
TJul8QKBgAxnQB8MA7zPULu1clyaJLdtEdRPkKWN7lKYptc0e/VHfSsKxseWkfqi
zgXZ51kQTrT6Zb6HYRfwC1mMXHWRKRyYjAnCxVim6YQd+KVT49iRDDAiIFoMGA4i
vvcIlneqOZZPDIoKJ60IjO/DZHWkw5mLjaIrT+qQ3XAGdJA13hcm
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,18 @@
-----BEGIN CERTIFICATE-----
MIIC+zCCAeOgAwIBAgIJALbZEDvUQrFKMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV
BAMMCWxvY2FsaG9zdDAeFw0xNjAzMjgwMzMwNDFaFw0yNjAzMjYwMzMwNDFaMBQx
EjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBAMj1+xg4jVLzVnB5j7n1ul30WEE4BCzcNFxg5AOB5H5q+wje0YYiVFg6PQyv
GCipqIRXVRdVQ1hHSeunYGKe8lq3Sb1X8PUJ12v9uRbpS9DK1Owqk8rsPDu6sVTL
qKKgH1Z8yazzaS0AbXuA5e9gO/RzijbnpEP+quM4dueiMPVEJyLq+EoIQY+MM8MP
8dZzL4XZl7wL4UsCN7rPcO6W3tlnT0iO3h9c/Ym2hFhz+KNJ9KRRCvtPGZESigtK
bHsXH099WDo8v/Wp5/evBw/+JD0opxmCfHIBALHt9v53RvvsDZ1t33Rpu5C8znEY
Y2Ay7NgxhqjqoWJqA48lJeA0clsCAwEAAaNQME4wHQYDVR0OBBYEFC0bTU1Xofeh
NKIelashIsqKidDYMB8GA1UdIwQYMBaAFC0bTU1XofehNKIelashIsqKidDYMAwG
A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAAiJL8IMTwNX9XqQWYDFgkG4
AnrVwQhreAqC9rSxDCjqqnMHPHGzcCeDMLAMoh0kOy20nowUGNtCZ0uBvnX2q1bN
g1jt+GBcLJDR3LL4CpNOlm3YhOycuNfWMxTA7BXkmnSrZD/7KhArsBEY8aulxwKJ
HRgNlIwe1oFD1YdX1BS5pp4t25B6Vq4A3FMMUkVoWE688nE168hvQgwjrHkgHhwe
eN8lGE2DhFraXnWmDMdwaHD3HRFGhyppIFN+f7BqbWX9gM+T2YRTfObIXLWbqJLD
3Mk/NkxqVcg4eY54wJ1ufCUGAYAIaY6fQqiNUz8nhwK3t45NBVT9y/uJXqnTLyY=
-----END CERTIFICATE-----

27
certificate/localhost.key Normal file
View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAyPX7GDiNUvNWcHmPufW6XfRYQTgELNw0XGDkA4Hkfmr7CN7R
hiJUWDo9DK8YKKmohFdVF1VDWEdJ66dgYp7yWrdJvVfw9QnXa/25FulL0MrU7CqT
yuw8O7qxVMuooqAfVnzJrPNpLQBte4Dl72A79HOKNuekQ/6q4zh256Iw9UQnIur4
SghBj4wzww/x1nMvhdmXvAvhSwI3us9w7pbe2WdPSI7eH1z9ibaEWHP4o0n0pFEK
+08ZkRKKC0psexcfT31YOjy/9ann968HD/4kPSinGYJ8cgEAse32/ndG++wNnW3f
dGm7kLzOcRhjYDLs2DGGqOqhYmoDjyUl4DRyWwIDAQABAoIBAGTKqsN9KbSfA42q
CqI0UuLouJMNa1qsnz5uAi6YKWgWdA4A44mpEjCmFRSVhUJvxWuK+cyYIQzXxIWD
D16nZdqF72AeCWZ9JySsvvZ00GfKM3y35iRy08sJWgOzmcLnGJCiSeyKsQe3HTJC
dhDXbXqvsHTVPZg01LTeDxUiTffU8NMKqR2AecQ2sTDwXEhAnTyAtnzl/XaBgFzu
U6G7FzGM5y9bxkfQVkvy+DEJkHGNOjzwcVfByyVl610ixmG1vmxVj9PbWmIPsUV8
ySmjhvDQbOfoxW0h9vTlTqGtQcBw962osnDDMWFCdM7lzO0T7RRnPVGIRpCJOKhq
keqHKwECgYEA8wwI/iZughoTXTNG9LnQQ/WAtsqO80EjMTUheo5I1kOzmUz09pyh
iAsUDoN0/26tZ5WNjlnyZu7dvTc/x3dTZpmNnoo8gcVbQNECDRzqfuQ9PPXm1SN5
6peBqAvBv78hjV05aXzPG/VBbeig7l299EarEA+a/oH3KrgDoqVqE0ECgYEA06vA
YJmgg4fZRucAYoaYsLz9Z9rCFjTe1PBTmUJkbOR8vFIHHTTEWi/SuxXL0wDSeoE2
7BQm86gCC7/KgRdrzoBqZ5qS9Mv2dsLgY635VSgjjfZkVLiH1VRRpSQObYnfoysg
gatcHSKMExd4SLQByAuImXP+L5ayDBcEJfbqSpsCgYB78Is1b0uzNLDjOh7Y9Vhr
D2qPzEORcIoNsdZctOoXuXaAmmngyIbm5R9ZN1gWWc47oFwLV3rxWqXgs6fmg8cX
7v309vFcC9Q4/Vxaa4B5LNK9n3gTAIBPTOtlUnl+2my1tfBtBqRm0W6IKbTHWS5g
vxjEm/CiEIyGUEgqTMgHAQKBgBKuXdQoutng63QufwIzDtbKVzMLQ4XiNKhmbXph
OavCnp+gPbB+L7Yl8ltAmTSOJgVZ0hcT0DxA361Zx+2Mu58GBl4OblnchmwE1vj1
KcQyPrEQxdoUTyiswGfqvrs8J9imvb+z9/U6T1KAB8Wi3WViXzPr4MsiaaRXg642
FIdxAoGAZ7/735dkhJcyOfs+LKsLr68JSstoorXOYvdMu1+JGa9iLuhnHEcMVWC8
IuihzPfloZtMbGYkZJn8l3BeGd8hmfFtgTgZGPoVRetft2LDFLnPxp2sEH5OFLsQ
R+K/kAOul8eStWuMXOFA9pMzGkGEgIFJMJOyaJON3kedQI8deCM=
-----END RSA PRIVATE KEY-----

45
gorush/config_test.go Normal file
View File

@ -0,0 +1,45 @@
package gopush
import (
"github.com/stretchr/testify/assert"
"io/ioutil"
"log"
"os"
"testing"
)
// Test file is missing
func TestMissingFile(t *testing.T) {
filename := "test"
_, err := LoadConfYaml(filename)
assert.NotNil(t, err)
}
// Test wrong json format
func TestWrongYAMLormat(t *testing.T) {
content := []byte(`Wrong format`)
filename := "tempfile"
if err := ioutil.WriteFile(filename, content, 0644); err != nil {
log.Fatalf("WriteFile %s: %v", filename, err)
}
// clean up
defer os.Remove(filename)
// parse JSON format error
_, err := LoadConfYaml(filename)
assert.NotNil(t, err)
}
// Test config file.
func TestReadConfig(t *testing.T) {
config, err := LoadConfYaml("../config/config.yaml")
assert.Nil(t, err)
assert.Equal(t, "8088", config.Core.Port)
assert.False(t, config.Android.Enabled)
}

View File

@ -13,7 +13,21 @@ type ExtendJSON struct {
Value string `json:"val"` Value string `json:"val"`
} }
type alert struct { const (
// PriorityLow will tell APNs to send the push message at a time that takes
// into account power considerations for the device. Notifications with this
// priority might be grouped and delivered in bursts. They are throttled, and
// in some cases are not delivered.
ApnsPriorityLow = 5
// PriorityHigh will tell APNs to send the push message immediately.
// Notifications with this priority must trigger an alert, sound, or badge on
// the target device. It is an error to use this priority for a push
// notification that contains only the content-available key.
ApnsPriorityHigh = 10
)
type Alert struct {
Action string `json:"action,omitempty"` Action string `json:"action,omitempty"`
ActionLocKey string `json:"action-loc-key,omitempty"` ActionLocKey string `json:"action-loc-key,omitempty"`
Body string `json:"body,omitempty"` Body string `json:"body,omitempty"`
@ -48,18 +62,16 @@ type RequestPushNotification struct {
Topic string `json:"topic,omitempty"` Topic string `json:"topic,omitempty"`
Badge int `json:"badge,omitempty"` Badge int `json:"badge,omitempty"`
Sound string `json:"sound,omitempty"` Sound string `json:"sound,omitempty"`
Expiry int `json:"expiry,omitempty"`
Retry int `json:"retry,omitempty"`
Category string `json:"category,omitempty"` Category string `json:"category,omitempty"`
URLArgs []string `json:"url-args,omitempty"` URLArgs []string `json:"url-args,omitempty"`
Extend []ExtendJSON `json:"extend,omitempty"` Extend []ExtendJSON `json:"extend,omitempty"`
Alert alert `json:"alert,omitempty"` Alert Alert `json:"alert,omitempty"`
// meta // meta
IDs []uint64 `json:"seq_id,omitempty"` IDs []uint64 `json:"seq_id,omitempty"`
} }
func InitAPNSClient() { func InitAPNSClient() error {
if PushConf.Ios.Enabled { if PushConf.Ios.Enabled {
var err error var err error
@ -68,7 +80,7 @@ func InitAPNSClient() {
if err != nil { if err != nil {
log.Println("Cert Error:", err) log.Println("Cert Error:", err)
return return err
} }
if PushConf.Ios.Production { if PushConf.Ios.Production {
@ -77,6 +89,8 @@ func InitAPNSClient() {
ApnsClient = apns.NewClient(CertificatePemIos).Development() ApnsClient = apns.NewClient(CertificatePemIos).Development()
} }
} }
return nil
} }
func pushNotification(notification RequestPushNotification) bool { func pushNotification(notification RequestPushNotification) bool {
@ -100,13 +114,10 @@ func pushNotification(notification RequestPushNotification) bool {
return success return success
} }
func pushNotificationIos(req RequestPushNotification) bool { // The iOS Notification Payload
// ref: https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/TheNotificationPayload.html
// The Remote Notification Payload func GetIOSNotification(req RequestPushNotification) *apns.Notification {
// https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/TheNotificationPayload.html
for _, token := range req.Tokens {
notification := &apns.Notification{} notification := &apns.Notification{}
notification.DeviceToken = token
if len(req.ApnsID) > 0 { if len(req.ApnsID) > 0 {
notification.ApnsID = req.ApnsID notification.ApnsID = req.ApnsID
@ -150,8 +161,13 @@ func pushNotificationIos(req RequestPushNotification) bool {
payload.AlertTitleLocKey(req.Alert.TitleLocKey) payload.AlertTitleLocKey(req.Alert.TitleLocKey)
} }
if len(req.Alert.LocArgs) > 0 { // Need send PR to apns2 repo.
payload.AlertTitleLocArgs(req.Alert.LocArgs) // if len(req.Alert.LocArgs) > 0 {
// payload.AlertLocArgs(req.Alert.LocArgs)
// }
if len(req.Alert.TitleLocArgs) > 0 {
payload.AlertTitleLocArgs(req.Alert.TitleLocArgs)
} }
if len(req.Alert.Body) > 0 { if len(req.Alert.Body) > 0 {
@ -186,6 +202,16 @@ func pushNotificationIos(req RequestPushNotification) bool {
notification.Payload = payload notification.Payload = payload
return notification
}
func pushNotificationIos(req RequestPushNotification) bool {
notification := GetIOSNotification(req)
for _, token := range req.Tokens {
notification.DeviceToken = token
// send ios notification // send ios notification
res, err := ApnsClient.Push(notification) res, err := ApnsClient.Push(notification)
@ -197,17 +223,15 @@ func pushNotificationIos(req RequestPushNotification) bool {
if res.Sent() { if res.Sent() {
log.Println("APNs ID:", res.ApnsID) log.Println("APNs ID:", res.ApnsID)
return true
} }
} }
return true return true
} }
func pushNotificationAndroid(req RequestPushNotification) bool { // HTTP Connection Server Reference for Android
// https://developers.google.com/cloud-messaging/http-server-ref
// HTTP Connection Server Reference for Android func GetAndroidNotification(req RequestPushNotification) gcm.HttpMessage {
// https://developers.google.com/cloud-messaging/http-server-ref
notification := gcm.HttpMessage{} notification := gcm.HttpMessage{}
notification.RegistrationIds = req.Tokens notification.RegistrationIds = req.Tokens
@ -255,21 +279,30 @@ func pushNotificationAndroid(req RequestPushNotification) bool {
notification.Notification.Body = req.Message notification.Notification.Body = req.Message
} }
return notification
}
func pushNotificationAndroid(req RequestPushNotification) bool {
notification := GetAndroidNotification(req)
res, err := gcm.SendHttp(PushConf.Android.ApiKey, notification) res, err := gcm.SendHttp(PushConf.Android.ApiKey, notification)
log.Printf("Success count: %d, Failure count: %d", res.Success, res.Failure)
if err != nil { if err != nil {
log.Println(err) log.Println("GCM Server Error Message: " + err.Error())
return false return false
} }
if res.Error != "" { if res.Error != "" {
log.Println("GCM Error Message: " + res.Error) log.Println("GCM Http Error Message: " + res.Error)
return false
} }
if res.Success > 0 { if res.Success > 0 {
log.Printf("Success count: %d, Failure count: %d", res.Success, res.Failure)
return true return true
} }

158
gorush/notification_test.go Normal file
View File

@ -0,0 +1,158 @@
package gopush
import (
"encoding/json"
"github.com/buger/jsonparser"
"github.com/google/go-gcm"
"github.com/stretchr/testify/assert"
"log"
"testing"
)
func TestIOSNotificationStructure(t *testing.T) {
var dat map[string]interface{}
test := "test"
message := "Welcome notification Server"
req := RequestPushNotification{
ApnsID: test,
Topic: test,
Priority: "normal",
Message: message,
Badge: 1,
Sound: test,
ContentAvailable: true,
Extend: []ExtendJSON{
{
Key: "key1",
Value: "1",
},
{
Key: "key2",
Value: "2",
},
},
Category: test,
URLArgs: []string{"a", "b"},
}
notification := GetIOSNotification(req)
dump, _ := json.Marshal(notification.Payload)
data := []byte(string(dump))
if err := json.Unmarshal(data, &dat); err != nil {
log.Println(err)
panic(err)
}
alert, _ := jsonparser.GetString(data, "aps", "alert")
badge, _ := jsonparser.GetInt(data, "aps", "badge")
sound, _ := jsonparser.GetString(data, "aps", "sound")
contentAvailable, _ := jsonparser.GetInt(data, "aps", "content-available")
category, _ := jsonparser.GetString(data, "aps", "category")
key1 := dat["key1"].(string)
key2 := dat["key2"].(string)
aps := dat["aps"].(map[string]interface{})
urlArgs := aps["url-args"].([]interface{})
assert.Equal(t, test, notification.ApnsID)
assert.Equal(t, test, notification.Topic)
assert.Equal(t, ApnsPriorityLow, notification.Priority)
assert.Equal(t, message, alert)
assert.Equal(t, 1, int(badge))
assert.Equal(t, test, sound)
assert.Equal(t, 1, int(contentAvailable))
assert.Equal(t, "1", key1)
assert.Equal(t, "2", key2)
assert.Equal(t, test, category)
assert.Contains(t, urlArgs, "a")
assert.Contains(t, urlArgs, "b")
}
func TestIOSAlertNotificationStructure(t *testing.T) {
var dat map[string]interface{}
test := "test"
req := RequestPushNotification{
Alert: Alert{
Action: test,
ActionLocKey: test,
Body: test,
LaunchImage: test,
LocArgs: []string{"a", "b"},
LocKey: test,
Title: test,
TitleLocArgs: []string{"a", "b"},
TitleLocKey: test,
},
}
notification := GetIOSNotification(req)
dump, _ := json.Marshal(notification.Payload)
data := []byte(string(dump))
if err := json.Unmarshal(data, &dat); err != nil {
log.Println(err)
panic(err)
}
action, _ := jsonparser.GetString(data, "aps", "alert", "action")
actionLocKey, _ := jsonparser.GetString(data, "aps", "alert", "action-loc-key")
body, _ := jsonparser.GetString(data, "aps", "alert", "body")
launchImage, _ := jsonparser.GetString(data, "aps", "alert", "launch-image")
locKey, _ := jsonparser.GetString(data, "aps", "alert", "loc-key")
title, _ := jsonparser.GetString(data, "aps", "alert", "title")
titleLocKey, _ := jsonparser.GetString(data, "aps", "alert", "title-loc-key")
aps := dat["aps"].(map[string]interface{})
alert := aps["alert"].(map[string]interface{})
titleLocArgs := alert["title-loc-args"].([]interface{})
assert.Equal(t, test, action)
assert.Equal(t, test, actionLocKey)
assert.Equal(t, test, body)
assert.Equal(t, test, launchImage)
assert.Equal(t, test, locKey)
assert.Equal(t, test, title)
assert.Equal(t, test, titleLocKey)
assert.Contains(t, titleLocArgs, "a")
assert.Contains(t, titleLocArgs, "b")
}
func TestAndroidNotificationStructure(t *testing.T) {
test := "test"
req := RequestPushNotification{
Tokens: []string{"a", "b"},
Message: "Welcome",
To: test,
Priority: "high",
CollapseKey: "1",
ContentAvailable: true,
DelayWhileIdle: true,
TimeToLive: 100,
RestrictedPackageName: test,
DryRun: true,
Data: map[string]interface{}{
"a": "1",
"b": "2",
},
Notification: gcm.Notification{
Title: test,
},
}
notification := GetAndroidNotification(req)
assert.Equal(t, test, notification.To)
assert.Equal(t, "high", notification.Priority)
assert.Equal(t, "1", notification.CollapseKey)
assert.True(t, notification.ContentAvailable)
assert.True(t, notification.DelayWhileIdle)
assert.Equal(t, 100, int(notification.TimeToLive))
assert.Equal(t, test, notification.RestrictedPackageName)
assert.True(t, notification.DryRun)
assert.Equal(t, test, notification.Notification.Title)
assert.Equal(t, "Welcome", notification.Notification.Body)
}

View File

@ -56,10 +56,13 @@ func GetMainEngine() *gin.Engine {
return r return r
} }
func RunHTTPServer() { func RunHTTPServer() error {
var err error
if PushConf.Core.SSL && PushConf.Core.CertPath != "" && PushConf.Core.KeyPath != "" { if PushConf.Core.SSL && PushConf.Core.CertPath != "" && PushConf.Core.KeyPath != "" {
GetMainEngine().RunTLS(":"+PushConf.Core.Port, PushConf.Core.CertPath, PushConf.Core.KeyPath) err = GetMainEngine().RunTLS(":"+PushConf.Core.Port, PushConf.Core.CertPath, PushConf.Core.KeyPath)
} else { } else {
GetMainEngine().Run(":" + PushConf.Core.Port) err = GetMainEngine().Run(":" + PushConf.Core.Port)
} }
return err
} }

View File

@ -3,10 +3,14 @@ package gopush
import ( import (
"github.com/appleboy/gofight" "github.com/appleboy/gofight"
"github.com/buger/jsonparser" "github.com/buger/jsonparser"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"io/ioutil"
"net/http" "net/http"
"testing" "os"
"runtime" "runtime"
"testing"
"time"
) )
var go_version = runtime.Version() var go_version = runtime.Version()
@ -16,6 +20,56 @@ func initTest() {
PushConf.Core.Mode = "test" PushConf.Core.Mode = "test"
} }
func testRequest(t *testing.T, url string) {
resp, err := http.Get(url)
defer resp.Body.Close()
assert.NoError(t, err)
_, ioerr := ioutil.ReadAll(resp.Body)
assert.NoError(t, ioerr)
assert.Equal(t, "200 OK", resp.Status, "should get a 200")
}
func TestPrintGoPushVersion(t *testing.T) {
PrintGoPushVersion()
}
func TestRunNormalServer(t *testing.T) {
initTest()
router := gin.New()
go func() {
assert.NoError(t, RunHTTPServer())
}()
// have to wait for the goroutine to start and run the server
// otherwise the main thread will complete
time.Sleep(5 * time.Millisecond)
assert.Error(t, router.Run(":8088"))
testRequest(t, "http://localhost:8088/api/status")
}
// func TestRunTLSServer(t *testing.T) {
// initTest()
// PushConf.Core.SSL = true
// PushConf.Core.Port = "8087"
// PushConf.Core.CertPath = "../certificate/localhost.cert"
// PushConf.Core.KeyPath = "../certificate/localhost.key"
// router := gin.New()
// go func() {
// assert.NoError(t, RunHTTPServer())
// }()
// // have to wait for the goroutine to start and run the server
// // otherwise the main thread will complete
// time.Sleep(5 * time.Millisecond)
// assert.Error(t, router.Run(":8087"))
// testRequest(t, "https://localhost:8087/api/status")
// }
func TestRootHandler(t *testing.T) { func TestRootHandler(t *testing.T) {
initTest() initTest()
@ -48,7 +102,7 @@ func TestAPIStatusHandler(t *testing.T) {
}) })
} }
func TestPushHandler(t *testing.T) { func TestMissingParameterPushHandler(t *testing.T) {
initTest() initTest()
r := gofight.New() r := gofight.New()
@ -63,3 +117,157 @@ func TestPushHandler(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, r.Code) assert.Equal(t, http.StatusBadRequest, r.Code)
}) })
} }
func TestDisabledIosPushHandler(t *testing.T) {
initTest()
PushConf.Ios.Enabled = false
InitAPNSClient()
r := gofight.New()
r.POST("/api/push").
SetJSON(gofight.D{
"tokens": []string{"11aa01229f15f0f0c52029d8cf8cd0aeaf2365fe4cebc4af26cd6d76b7919ef7"},
"platform": 1,
"message": "Welcome",
}).
Run(GetMainEngine(), func(r gofight.HttpResponse, rq gofight.HttpRequest) {
assert.Equal(t, http.StatusOK, r.Code)
})
}
func TestMissingIosCertificate(t *testing.T) {
initTest()
PushConf.Ios.Enabled = true
PushConf.Ios.PemKeyPath = "test"
err := InitAPNSClient()
assert.Error(t, err)
}
func TestIosPushDevelopment(t *testing.T) {
initTest()
PushConf.Ios.Enabled = true
PushConf.Ios.PemKeyPath = "../certificate/certificate-valid.pem"
InitAPNSClient()
r := gofight.New()
r.POST("/api/push").
SetJSON(gofight.D{
"tokens": []string{"11aa01229f15f0f0c52029d8cf8cd0aeaf2365fe4cebc4af26cd6d76b7919ef7"},
"platform": 1,
"message": "Welcome",
}).
Run(GetMainEngine(), func(r gofight.HttpResponse, rq gofight.HttpRequest) {
assert.Equal(t, http.StatusOK, r.Code)
})
}
func TestIosPushProduction(t *testing.T) {
initTest()
PushConf.Ios.Enabled = true
PushConf.Ios.Production = true
PushConf.Ios.PemKeyPath = "../certificate/certificate-valid.pem"
InitAPNSClient()
r := gofight.New()
r.POST("/api/push").
SetJSON(gofight.D{
"tokens": []string{"11aa01229f15f0f0c52029d8cf8cd0aeaf2365fe4cebc4af26cd6d76b7919ef7"},
"platform": 1,
"message": "Welcome",
}).
Run(GetMainEngine(), func(r gofight.HttpResponse, rq gofight.HttpRequest) {
assert.Equal(t, http.StatusOK, r.Code)
})
}
func TestDisabledAndroidPushHandler(t *testing.T) {
initTest()
PushConf.Android.Enabled = false
r := gofight.New()
r.POST("/api/push").
SetJSON(gofight.D{
"tokens": []string{"aaaaaa", "bbbbb"},
"platform": 2,
"message": "Welcome",
}).
Run(GetMainEngine(), func(r gofight.HttpResponse, rq gofight.HttpRequest) {
assert.Equal(t, http.StatusOK, r.Code)
})
}
func TestAndroidWrongAPIKey(t *testing.T) {
initTest()
PushConf.Android.Enabled = true
PushConf.Android.ApiKey = os.Getenv("ANDROID_API_KEY") + "a"
r := gofight.New()
r.POST("/api/push").
SetJSON(gofight.D{
"tokens": []string{"aaaaaa", "bbbbb"},
"platform": 2,
"message": "Welcome",
}).
Run(GetMainEngine(), func(r gofight.HttpResponse, rq gofight.HttpRequest) {
assert.Equal(t, http.StatusOK, r.Code)
})
}
func TestAndroidWrongToken(t *testing.T) {
initTest()
PushConf.Android.Enabled = true
PushConf.Android.ApiKey = os.Getenv("ANDROID_API_KEY")
r := gofight.New()
r.POST("/api/push").
SetJSON(gofight.D{
"tokens": []string{"aaaaaa", "bbbbb"},
"platform": 2,
"message": "Welcome",
}).
Run(GetMainEngine(), func(r gofight.HttpResponse, rq gofight.HttpRequest) {
assert.Equal(t, http.StatusOK, r.Code)
})
}
func TestAndroidRightToken(t *testing.T) {
initTest()
PushConf.Android.Enabled = true
PushConf.Android.ApiKey = os.Getenv("ANDROID_API_KEY")
android_token := os.Getenv("ANDROID_TEST_TOKEN")
r := gofight.New()
r.POST("/api/push").
SetJSON(gofight.D{
"tokens": []string{android_token, "bbbbb"},
"platform": 2,
"message": "Welcome",
}).
Run(GetMainEngine(), func(r gofight.HttpResponse, rq gofight.HttpRequest) {
assert.Equal(t, http.StatusOK, r.Code)
})
}