コード例 #1
0
func TestRequestHandlerAuth(t *testing.T) {
	c := &config.Config{
		NetworkPassphrase: "Test SDF Network ; September 2015",
		Keys: config.Keys{
			// GBYJZW5XFAI6XV73H5SAIUYK6XZI4CGGVBUBO3ANA2SV7KKDAXTV6AEB
			SigningSeed: "SDWTLFPALQSP225BSMX7HPZ7ZEAYSUYNDLJ5QI3YGVBNRUIIELWH3XUV",
		},
	}

	mockHTTPClient := new(mocks.MockHTTPClient)
	mockEntityManager := new(mocks.MockEntityManager)
	mockRepository := new(mocks.MockRepository)
	mockFederationResolver := new(mocks.MockFederationResolver)
	mockSignerVerifier := new(mocks.MockSignerVerifier)
	mockStellartomlResolver := new(mocks.MockStellartomlResolver)
	requestHandler := RequestHandler{}

	// Inject mocks
	var g inject.Graph

	err := g.Provide(
		&inject.Object{Value: &requestHandler},
		&inject.Object{Value: c},
		&inject.Object{Value: mockHTTPClient},
		&inject.Object{Value: mockEntityManager},
		&inject.Object{Value: mockRepository},
		&inject.Object{Value: mockFederationResolver},
		&inject.Object{Value: mockSignerVerifier},
		&inject.Object{Value: mockStellartomlResolver},
	)
	if err != nil {
		panic(err)
	}

	if err := g.Populate(); err != nil {
		panic(err)
	}

	httpHandle := func(w http.ResponseWriter, r *http.Request) {
		requestHandler.HandlerAuth(web.C{}, w, r)
	}

	testServer := httptest.NewServer(http.HandlerFunc(httpHandle))
	defer testServer.Close()

	Convey("Given auth request (no sanctions check)", t, func() {
		Convey("When data param is missing", func() {
			statusCode, response := net.GetResponse(testServer, url.Values{})
			responseString := strings.TrimSpace(string(response))
			assert.Equal(t, 400, statusCode)
			expected := test.StringToJSONMap(`{
  "code": "missing_parameter",
  "message": "Required parameter is missing.",
  "data": {
    "name": "data"
  }
}`)
			assert.Equal(t, expected, test.StringToJSONMap(responseString))
		})

		Convey("When data is invalid", func() {
			params := url.Values{
				"data": {"hello world"},
				"sig":  {"bad sig"},
			}

			statusCode, response := net.GetResponse(testServer, params)
			responseString := strings.TrimSpace(string(response))
			assert.Equal(t, 400, statusCode)
			expected := test.StringToJSONMap(`{
  "code": "invalid_parameter",
  "message": "Invalid parameter.",
  "data": {
    "name": "data"
  }
}`)
			assert.Equal(t, expected, test.StringToJSONMap(responseString))
		})

		Convey("When sender's stellar.toml does not contain signing key", func() {
			mockStellartomlResolver.On(
				"GetStellarTomlByAddress",
				"alice*stellar.org",
			).Return(stellartoml.StellarToml{}, nil).Once()

			params := url.Values{
				"data": {"{\"sender\":\"alice*stellar.org\",\"need_info\":false,\"tx\":\"AAAAAC3/58Z9rycNLmF6voWX9VmDETFVGhFoWf66mcMuir/DAAAAZAAAAAAAAAAAAAAAAAAAAAO5TSe5k00+CKUuUtfafav6xITv43pTgO6QiPes4u/N6QAAAAEAAAAAAAAAAgAAAAFVU0QAAAAAAEbpO2riZmlZMkHuBxUBYAAas3hWyo9VL1IOdnfXAVFBAAAAADuaygAAAAAAGVL83DJFwH0sKmy6AIgJYD7GexiD0YuzSMioBCAUOJwAAAABVVNEAAAAAAAZUvzcMkXAfSwqbLoAiAlgPsZ7GIPRi7NIyKgEIBQ4nAAAAAAL68IAAAAAAgAAAAAAAAABRVVSAAAAAAALt4SwWfv1PIJvDRMenW0zu91YxZbphRFLA4O+gbAaigAAAAA=\",\"memo\":\"hello world\"}"},
				"sig":  {"bad sig"},
			}

			statusCode, response := net.GetResponse(testServer, params)
			responseString := strings.TrimSpace(string(response))
			assert.Equal(t, 400, statusCode)
			expected := test.StringToJSONMap(`{
  "code": "invalid_parameter",
  "message": "Invalid parameter.",
  "data": {
    "name": "data.sender"
  }
}`)
			assert.Equal(t, expected, test.StringToJSONMap(responseString))
		})

		Convey("When signature is invalid", func() {
			mockStellartomlResolver.On(
				"GetStellarTomlByAddress",
				"alice*stellar.org",
			).Return(stellartoml.StellarToml{
				SigningKey: "GBYJZW5XFAI6XV73H5SAIUYK6XZI4CGGVBUBO3ANA2SV7KKDAXTV6AEB",
			}, nil).Once()

			params := url.Values{
				"data": {"{\"sender\":\"alice*stellar.org\",\"need_info\":false,\"tx\":\"AAAAAC3/58Z9rycNLmF6voWX9VmDETFVGhFoWf66mcMuir/DAAAAZAAAAAAAAAAAAAAAAAAAAAO5TSe5k00+CKUuUtfafav6xITv43pTgO6QiPes4u/N6QAAAAEAAAAAAAAAAgAAAAFVU0QAAAAAAEbpO2riZmlZMkHuBxUBYAAas3hWyo9VL1IOdnfXAVFBAAAAADuaygAAAAAAGVL83DJFwH0sKmy6AIgJYD7GexiD0YuzSMioBCAUOJwAAAABVVNEAAAAAAAZUvzcMkXAfSwqbLoAiAlgPsZ7GIPRi7NIyKgEIBQ4nAAAAAAL68IAAAAAAgAAAAAAAAABRVVSAAAAAAALt4SwWfv1PIJvDRMenW0zu91YxZbphRFLA4O+gbAaigAAAAA=\",\"memo\":\"hello world\"}"},
				"sig":  {"ACamNqa0dF8gf97URhFVKWSD7fmvZKc5At+8dCLM5ySR0HsHySF3G2WuwYP2nKjeqjKmu3U9Z3+u1P10w1KBCA=="},
			}

			mockSignerVerifier.On(
				"Verify",
				"GBYJZW5XFAI6XV73H5SAIUYK6XZI4CGGVBUBO3ANA2SV7KKDAXTV6AEB",
				mock.AnythingOfType("[]uint8"),
				mock.AnythingOfType("[]uint8"),
			).Return(errors.New("Verify error")).Once()

			statusCode, response := net.GetResponse(testServer, params)
			responseString := strings.TrimSpace(string(response))
			assert.Equal(t, 400, statusCode)
			expected := test.StringToJSONMap(`{
  "code": "invalid_parameter",
  "message": "Invalid parameter.",
  "data": {
    "name": "sig"
  }
}`)
			assert.Equal(t, expected, test.StringToJSONMap(responseString))
		})

		Convey("When all params are valid", func() {
			params := url.Values{
				"data": {"{\"sender\":\"alice*stellar.org\",\"need_info\":false,\"tx\":\"AAAAAC3/58Z9rycNLmF6voWX9VmDETFVGhFoWf66mcMuir/DAAAAZAAAAAAAAAAAAAAAAAAAAANEE2+jVbNnihFGrRb36GSelPtPwh/nfoMQwGD2HKr/igAAAAEAAAAAAAAAAgAAAAFVU0QAAAAAAEbpO2riZmlZMkHuBxUBYAAas3hWyo9VL1IOdnfXAVFBAAAAADuaygAAAAAAGVL83DJFwH0sKmy6AIgJYD7GexiD0YuzSMioBCAUOJwAAAABVVNEAAAAAAAZUvzcMkXAfSwqbLoAiAlgPsZ7GIPRi7NIyKgEIBQ4nAAAAAAL68IAAAAAAgAAAAAAAAABRVVSAAAAAAALt4SwWfv1PIJvDRMenW0zu91YxZbphRFLA4O+gbAaigAAAAA=\",\"memo\":\"{}\"}"},
				"sig":  {"ACamNqa0dF8gf97URhFVKWSD7fmvZKc5At+8dCLM5ySR0HsHySF3G2WuwYP2nKjeqjKmu3U9Z3+u1P10w1KBCA=="},
			}

			mockStellartomlResolver.On(
				"GetStellarTomlByAddress",
				"alice*stellar.org",
			).Return(stellartoml.StellarToml{
				SigningKey: "GBYJZW5XFAI6XV73H5SAIUYK6XZI4CGGVBUBO3ANA2SV7KKDAXTV6AEB",
			}, nil).Once()

			mockSignerVerifier.On(
				"Verify",
				"GBYJZW5XFAI6XV73H5SAIUYK6XZI4CGGVBUBO3ANA2SV7KKDAXTV6AEB",
				mock.AnythingOfType("[]uint8"),
				mock.AnythingOfType("[]uint8"),
			).Return(nil).Once()

			Convey("it returns AuthResponse", func() {
				authorizedTransaction := &entities.AuthorizedTransaction{
					TransactionID:  "29ec92f95b00dd8e8bbb0d2a2fc90db8ed5b26c396c44ac978bb13ccd8d25524",
					Memo:           "RBNvo1WzZ4oRRq0W9+hknpT7T8If536DEMBg9hyq/4o=",
					TransactionXdr: "AAAAAC3/58Z9rycNLmF6voWX9VmDETFVGhFoWf66mcMuir/DAAAAZAAAAAAAAAAAAAAAAAAAAANEE2+jVbNnihFGrRb36GSelPtPwh/nfoMQwGD2HKr/igAAAAEAAAAAAAAAAgAAAAFVU0QAAAAAAEbpO2riZmlZMkHuBxUBYAAas3hWyo9VL1IOdnfXAVFBAAAAADuaygAAAAAAGVL83DJFwH0sKmy6AIgJYD7GexiD0YuzSMioBCAUOJwAAAABVVNEAAAAAAAZUvzcMkXAfSwqbLoAiAlgPsZ7GIPRi7NIyKgEIBQ4nAAAAAAL68IAAAAAAgAAAAAAAAABRVVSAAAAAAALt4SwWfv1PIJvDRMenW0zu91YxZbphRFLA4O+gbAaigAAAAA=",
					Data:           params["data"][0],
				}

				mockEntityManager.On(
					"Persist",
					mock.AnythingOfType("*entities.AuthorizedTransaction"),
				).Run(func(args mock.Arguments) {
					value := args.Get(0).(*entities.AuthorizedTransaction)
					assert.Equal(t, authorizedTransaction.TransactionID, value.TransactionID)
					assert.Equal(t, authorizedTransaction.Memo, value.Memo)
					assert.Equal(t, authorizedTransaction.TransactionXdr, value.TransactionXdr)
					assert.WithinDuration(t, time.Now(), value.AuthorizedAt, 2*time.Second)
					assert.Equal(t, authorizedTransaction.Data, value.Data)
				}).Return(nil).Once()

				statusCode, response := net.GetResponse(testServer, params)
				responseString := strings.TrimSpace(string(response))
				assert.Equal(t, 200, statusCode)
				expected := test.StringToJSONMap(`{
  "info_status": "ok",
  "tx_status": "ok"
}`)
				assert.Equal(t, expected, test.StringToJSONMap(responseString))
			})
		})
	})

	Convey("Given auth request (sanctions check)", t, func() {
		c.Callbacks = config.Callbacks{
			Sanctions: "http://sanctions",
			AskUser:   "******",
			FetchInfo: "http://fetch_info",
		}

		memoPreimage := memo.Memo{
			Transaction: memo.Transaction{
				Route:      "bob*acme.com",
				Note:       "Happy birthday",
				SenderInfo: "senderInfoJson",
				Extra:      "extra",
			},
		}

		Convey("When all params are valid (NeedInfo = `false`)", func() {
			authData := compliance.AuthData{
				Sender:   "alice*stellar.org",
				NeedInfo: false,
				Tx:       "AAAAAC3/58Z9rycNLmF6voWX9VmDETFVGhFoWf66mcMuir/DAAAAZAAAAAAAAAAAAAAAAAAAAANeCnOi6ZSpMNIMFUUVIfbBc5OA5kpzDbg+AJ6X8/WynAAAAAEAAAAAAAAAAgAAAAFVU0QAAAAAAEbpO2riZmlZMkHuBxUBYAAas3hWyo9VL1IOdnfXAVFBAAAAADuaygAAAAAAGVL83DJFwH0sKmy6AIgJYD7GexiD0YuzSMioBCAUOJwAAAABVVNEAAAAAAAZUvzcMkXAfSwqbLoAiAlgPsZ7GIPRi7NIyKgEIBQ4nAAAAAAL68IAAAAAAgAAAAAAAAABRVVSAAAAAAALt4SwWfv1PIJvDRMenW0zu91YxZbphRFLA4O+gbAaigAAAAA=",
				Memo:     string(memoPreimage.Marshal()),
			}

			params := url.Values{
				"data": {string(authData.Marshal())},
				"sig":  {"Q2cQVOn/A+aOxrLLeUPwHmBm3LMvlfXN8tDHo4Oi6SxWWueMTDfRkC4XvRX4emLij+Npo7/GfrZ82CnT5yB5Dg=="},
			}

			mockStellartomlResolver.On(
				"GetStellarTomlByAddress",
				"alice*stellar.org",
			).Return(stellartoml.StellarToml{
				SigningKey: "GBYJZW5XFAI6XV73H5SAIUYK6XZI4CGGVBUBO3ANA2SV7KKDAXTV6AEB",
			}, nil).Once()

			mockSignerVerifier.On(
				"Verify",
				"GBYJZW5XFAI6XV73H5SAIUYK6XZI4CGGVBUBO3ANA2SV7KKDAXTV6AEB",
				mock.AnythingOfType("[]uint8"),
				mock.AnythingOfType("[]uint8"),
			).Return(nil).Once()

			Convey("when sanctions server returns forbidden it returns tx_status `denied`", func() {
				mockHTTPClient.On(
					"PostForm",
					"http://sanctions",
					url.Values{"sender": {memoPreimage.Transaction.SenderInfo}},
				).Return(
					net.BuildHTTPResponse(403, "forbidden"),
					nil,
				).Once()

				statusCode, response := net.GetResponse(testServer, params)
				responseString := strings.TrimSpace(string(response))
				assert.Equal(t, 200, statusCode)
				expected := test.StringToJSONMap(`{
		  "info_status": "ok",
		  "tx_status": "denied"
		}`)
				assert.Equal(t, expected, test.StringToJSONMap(responseString))
			})

			Convey("when sanctions server returns accepted it returns tx_status `pending`", func() {
				mockHTTPClient.On(
					"PostForm",
					"http://sanctions",
					url.Values{"sender": {memoPreimage.Transaction.SenderInfo}},
				).Return(
					net.BuildHTTPResponse(202, "pending"),
					nil,
				).Once()

				statusCode, response := net.GetResponse(testServer, params)
				responseString := strings.TrimSpace(string(response))
				assert.Equal(t, 200, statusCode)
				expected := test.StringToJSONMap(`{
  "info_status": "ok",
  "tx_status": "pending",
  "pending": 600
}`)
				assert.Equal(t, expected, test.StringToJSONMap(responseString))
			})

			Convey("when sanctions server returns ok it returns tx_status `ok` and persists transaction", func() {
				mockHTTPClient.On(
					"PostForm",
					"http://sanctions",
					url.Values{"sender": {memoPreimage.Transaction.SenderInfo}},
				).Return(
					net.BuildHTTPResponse(200, "ok"),
					nil,
				).Once()

				authorizedTransaction := &entities.AuthorizedTransaction{
					TransactionID:  "f62589932eb9fcf0bf28fe95510bf614caf3169c67a85e75475a390a79b5ecc9",
					Memo:           "XgpzoumUqTDSDBVFFSH2wXOTgOZKcw24PgCel/P1spw=",
					TransactionXdr: "AAAAAC3/58Z9rycNLmF6voWX9VmDETFVGhFoWf66mcMuir/DAAAAZAAAAAAAAAAAAAAAAAAAAANeCnOi6ZSpMNIMFUUVIfbBc5OA5kpzDbg+AJ6X8/WynAAAAAEAAAAAAAAAAgAAAAFVU0QAAAAAAEbpO2riZmlZMkHuBxUBYAAas3hWyo9VL1IOdnfXAVFBAAAAADuaygAAAAAAGVL83DJFwH0sKmy6AIgJYD7GexiD0YuzSMioBCAUOJwAAAABVVNEAAAAAAAZUvzcMkXAfSwqbLoAiAlgPsZ7GIPRi7NIyKgEIBQ4nAAAAAAL68IAAAAAAgAAAAAAAAABRVVSAAAAAAALt4SwWfv1PIJvDRMenW0zu91YxZbphRFLA4O+gbAaigAAAAA=",
					Data:           params["data"][0],
				}

				mockEntityManager.On(
					"Persist",
					mock.AnythingOfType("*entities.AuthorizedTransaction"),
				).Run(func(args mock.Arguments) {
					value := args.Get(0).(*entities.AuthorizedTransaction)
					assert.Equal(t, authorizedTransaction.TransactionID, value.TransactionID)
					assert.Equal(t, authorizedTransaction.Memo, value.Memo)
					assert.Equal(t, authorizedTransaction.TransactionXdr, value.TransactionXdr)
					assert.WithinDuration(t, time.Now(), value.AuthorizedAt, 2*time.Second)
					assert.Equal(t, authorizedTransaction.Data, value.Data)
				}).Return(nil).Once()

				statusCode, response := net.GetResponse(testServer, params)
				responseString := strings.TrimSpace(string(response))
				assert.Equal(t, 200, statusCode)
				expected := test.StringToJSONMap(`{
  "info_status": "ok",
  "tx_status": "ok"
}`)
				assert.Equal(t, expected, test.StringToJSONMap(responseString))
			})
		})

		Convey("When all params are valid (NeedInfo = `true`)", func() {
			authData := compliance.AuthData{
				Sender:   "alice*stellar.org",
				NeedInfo: true,
				Tx:       "AAAAAC3/58Z9rycNLmF6voWX9VmDETFVGhFoWf66mcMuir/DAAAAZAAAAAAAAAAAAAAAAAAAAANeCnOi6ZSpMNIMFUUVIfbBc5OA5kpzDbg+AJ6X8/WynAAAAAEAAAAAAAAAAgAAAAFVU0QAAAAAAEbpO2riZmlZMkHuBxUBYAAas3hWyo9VL1IOdnfXAVFBAAAAADuaygAAAAAAGVL83DJFwH0sKmy6AIgJYD7GexiD0YuzSMioBCAUOJwAAAABVVNEAAAAAAAZUvzcMkXAfSwqbLoAiAlgPsZ7GIPRi7NIyKgEIBQ4nAAAAAAL68IAAAAAAgAAAAAAAAABRVVSAAAAAAALt4SwWfv1PIJvDRMenW0zu91YxZbphRFLA4O+gbAaigAAAAA=",
				Memo:     string(memoPreimage.Marshal()),
			}

			params := url.Values{
				"data": {string(authData.Marshal())},
				"sig":  {"Q2cQVOn/A+aOxrLLeUPwHmBm3LMvlfXN8tDHo4Oi6SxWWueMTDfRkC4XvRX4emLij+Npo7/GfrZ82CnT5yB5Dg=="},
			}

			mockStellartomlResolver.On(
				"GetStellarTomlByAddress",
				"alice*stellar.org",
			).Return(stellartoml.StellarToml{
				SigningKey: "GBYJZW5XFAI6XV73H5SAIUYK6XZI4CGGVBUBO3ANA2SV7KKDAXTV6AEB",
			}, nil).Once()

			mockSignerVerifier.On(
				"Verify",
				"GBYJZW5XFAI6XV73H5SAIUYK6XZI4CGGVBUBO3ANA2SV7KKDAXTV6AEB",
				mock.AnythingOfType("[]uint8"),
				mock.AnythingOfType("[]uint8"),
			).Return(nil).Once()

			// Make sanctions checks successful (tested in the previous test case)
			mockHTTPClient.On(
				"PostForm",
				"http://sanctions",
				url.Values{
					"sender": {memoPreimage.Transaction.SenderInfo},
				},
			).Return(
				net.BuildHTTPResponse(200, "ok"),
				nil,
			).Once()

			Convey("when ask_user server returns forbidden it returns info_status `denied`", func() {
				mockHTTPClient.On(
					"PostForm",
					"http://ask_user",
					url.Values{
						"sender":       {memoPreimage.Transaction.SenderInfo},
						"note":         {memoPreimage.Transaction.Note},
						"amount":       {"20.0000000"},
						"asset_code":   {"USD"},
						"asset_issuer": {"GAMVF7G4GJC4A7JMFJWLUAEIBFQD5RT3DCB5DC5TJDEKQBBACQ4JZVEE"},
					},
				).Return(
					net.BuildHTTPResponse(403, "forbidden"),
					nil,
				).Once()

				statusCode, response := net.GetResponse(testServer, params)
				responseString := strings.TrimSpace(string(response))
				assert.Equal(t, 200, statusCode)
				expected := test.StringToJSONMap(`{
  "info_status": "denied",
  "tx_status": "ok"
}`)
				assert.Equal(t, expected, test.StringToJSONMap(responseString))
			})

			Convey("when ask_user server returns pending it returns info_status `pending`", func() {
				mockHTTPClient.On(
					"PostForm",
					"http://ask_user",
					url.Values{
						"sender":       {memoPreimage.Transaction.SenderInfo},
						"note":         {memoPreimage.Transaction.Note},
						"amount":       {"20.0000000"},
						"asset_code":   {"USD"},
						"asset_issuer": {"GAMVF7G4GJC4A7JMFJWLUAEIBFQD5RT3DCB5DC5TJDEKQBBACQ4JZVEE"},
					},
				).Return(
					net.BuildHTTPResponse(202, "{\"pending\": 300}"),
					nil,
				).Once()

				statusCode, response := net.GetResponse(testServer, params)
				responseString := strings.TrimSpace(string(response))
				assert.Equal(t, 200, statusCode)
				expected := test.StringToJSONMap(`{
  "info_status": "pending",
  "tx_status": "ok",
  "pending": 300
}`)
				assert.Equal(t, expected, test.StringToJSONMap(responseString))
			})

			Convey("when ask_user server returns pending but invalid response body it returns info_status `pending` (600 seconds)", func() {
				mockHTTPClient.On(
					"PostForm",
					"http://ask_user",
					url.Values{
						"sender":       {memoPreimage.Transaction.SenderInfo},
						"note":         {memoPreimage.Transaction.Note},
						"amount":       {"20.0000000"},
						"asset_code":   {"USD"},
						"asset_issuer": {"GAMVF7G4GJC4A7JMFJWLUAEIBFQD5RT3DCB5DC5TJDEKQBBACQ4JZVEE"},
					},
				).Return(
					net.BuildHTTPResponse(202, "pending"),
					nil,
				).Once()

				statusCode, response := net.GetResponse(testServer, params)
				responseString := strings.TrimSpace(string(response))
				assert.Equal(t, 200, statusCode)
				expected := test.StringToJSONMap(`{
  "info_status": "pending",
  "tx_status": "ok",
  "pending": 600
}`)
				assert.Equal(t, expected, test.StringToJSONMap(responseString))
			})

			Convey("when ask_user server returns ok it returns info_status `ok` and DestInfo and persists transaction", func() {
				mockHTTPClient.On(
					"PostForm",
					"http://ask_user",
					url.Values{
						"sender":       {memoPreimage.Transaction.SenderInfo},
						"note":         {memoPreimage.Transaction.Note},
						"amount":       {"20.0000000"},
						"asset_code":   {"USD"},
						"asset_issuer": {"GAMVF7G4GJC4A7JMFJWLUAEIBFQD5RT3DCB5DC5TJDEKQBBACQ4JZVEE"},
					},
				).Return(
					net.BuildHTTPResponse(200, "ok"),
					nil,
				).Once()

				mockHTTPClient.On(
					"PostForm",
					"http://fetch_info",
					url.Values{"address": {"bob*acme.com"}},
				).Return(
					net.BuildHTTPResponse(200, "user data"),
					nil,
				).Once()

				authorizedTransaction := &entities.AuthorizedTransaction{
					TransactionID:  "f62589932eb9fcf0bf28fe95510bf614caf3169c67a85e75475a390a79b5ecc9",
					Memo:           "XgpzoumUqTDSDBVFFSH2wXOTgOZKcw24PgCel/P1spw=",
					TransactionXdr: "AAAAAC3/58Z9rycNLmF6voWX9VmDETFVGhFoWf66mcMuir/DAAAAZAAAAAAAAAAAAAAAAAAAAANeCnOi6ZSpMNIMFUUVIfbBc5OA5kpzDbg+AJ6X8/WynAAAAAEAAAAAAAAAAgAAAAFVU0QAAAAAAEbpO2riZmlZMkHuBxUBYAAas3hWyo9VL1IOdnfXAVFBAAAAADuaygAAAAAAGVL83DJFwH0sKmy6AIgJYD7GexiD0YuzSMioBCAUOJwAAAABVVNEAAAAAAAZUvzcMkXAfSwqbLoAiAlgPsZ7GIPRi7NIyKgEIBQ4nAAAAAAL68IAAAAAAgAAAAAAAAABRVVSAAAAAAALt4SwWfv1PIJvDRMenW0zu91YxZbphRFLA4O+gbAaigAAAAA=",
					Data:           params["data"][0],
				}

				mockEntityManager.On(
					"Persist",
					mock.AnythingOfType("*entities.AuthorizedTransaction"),
				).Run(func(args mock.Arguments) {
					value := args.Get(0).(*entities.AuthorizedTransaction)
					assert.Equal(t, authorizedTransaction.TransactionID, value.TransactionID)
					assert.Equal(t, authorizedTransaction.Memo, value.Memo)
					assert.Equal(t, authorizedTransaction.TransactionXdr, value.TransactionXdr)
					assert.WithinDuration(t, time.Now(), value.AuthorizedAt, 2*time.Second)
					assert.Equal(t, authorizedTransaction.Data, value.Data)
				}).Return(nil).Once()

				statusCode, response := net.GetResponse(testServer, params)
				responseString := strings.TrimSpace(string(response))
				assert.Equal(t, 200, statusCode)
				expected := test.StringToJSONMap(`{
  "info_status": "ok",
  "tx_status": "ok",
  "dest_info": "user data"
}`)
				assert.Equal(t, expected, test.StringToJSONMap(responseString))
			})

			Convey("When no callbacks.ask_user server", func() {
				c.Callbacks.AskUser = ""

				Convey("when FI allowed it returns info_status = `ok` and DestInfo and persists transaction", func() {
					mockRepository.On(
						"GetAllowedFiByDomain",
						"stellar.org", // sender = `alice*stellar.org`
					).Return(
						&entities.AllowedFi{}, // It just returns existing record
						nil,
					).Once()

					mockHTTPClient.On(
						"PostForm",
						"http://fetch_info",
						url.Values{"address": {"bob*acme.com"}},
					).Return(
						net.BuildHTTPResponse(200, "user data"),
						nil,
					).Once()

					authorizedTransaction := &entities.AuthorizedTransaction{
						TransactionID:  "f62589932eb9fcf0bf28fe95510bf614caf3169c67a85e75475a390a79b5ecc9",
						Memo:           "XgpzoumUqTDSDBVFFSH2wXOTgOZKcw24PgCel/P1spw=",
						TransactionXdr: "AAAAAC3/58Z9rycNLmF6voWX9VmDETFVGhFoWf66mcMuir/DAAAAZAAAAAAAAAAAAAAAAAAAAANeCnOi6ZSpMNIMFUUVIfbBc5OA5kpzDbg+AJ6X8/WynAAAAAEAAAAAAAAAAgAAAAFVU0QAAAAAAEbpO2riZmlZMkHuBxUBYAAas3hWyo9VL1IOdnfXAVFBAAAAADuaygAAAAAAGVL83DJFwH0sKmy6AIgJYD7GexiD0YuzSMioBCAUOJwAAAABVVNEAAAAAAAZUvzcMkXAfSwqbLoAiAlgPsZ7GIPRi7NIyKgEIBQ4nAAAAAAL68IAAAAAAgAAAAAAAAABRVVSAAAAAAALt4SwWfv1PIJvDRMenW0zu91YxZbphRFLA4O+gbAaigAAAAA=",
						Data:           params["data"][0],
					}

					mockEntityManager.On(
						"Persist",
						mock.AnythingOfType("*entities.AuthorizedTransaction"),
					).Run(func(args mock.Arguments) {
						value := args.Get(0).(*entities.AuthorizedTransaction)
						assert.Equal(t, authorizedTransaction.TransactionID, value.TransactionID)
						assert.Equal(t, authorizedTransaction.Memo, value.Memo)
						assert.Equal(t, authorizedTransaction.TransactionXdr, value.TransactionXdr)
						assert.WithinDuration(t, time.Now(), value.AuthorizedAt, 2*time.Second)
						assert.Equal(t, authorizedTransaction.Data, value.Data)
					}).Return(nil).Once()

					statusCode, response := net.GetResponse(testServer, params)
					responseString := strings.TrimSpace(string(response))
					assert.Equal(t, 200, statusCode)
					expected := test.StringToJSONMap(`{
  "info_status": "ok",
  "tx_status": "ok",
  "dest_info": "user data"
}`)
					assert.Equal(t, expected, test.StringToJSONMap(responseString))
				})

				Convey("when FI not allowed but User is allowed it returns info_status = `ok` and DestInfo and persists transaction", func() {
					mockRepository.On(
						"GetAllowedFiByDomain",
						"stellar.org", // sender = `alice*stellar.org`
					).Return(
						nil,
						nil,
					).Once()

					mockRepository.On(
						"GetAllowedUserByDomainAndUserID",
						"stellar.org", // sender = `alice*stellar.org`
						"alice",
					).Return(
						&entities.AllowedUser{},
						nil,
					).Once()

					mockHTTPClient.On(
						"PostForm",
						"http://fetch_info",
						url.Values{"address": {"bob*acme.com"}},
					).Return(
						net.BuildHTTPResponse(200, "user data"),
						nil,
					).Once()

					authorizedTransaction := &entities.AuthorizedTransaction{
						TransactionID:  "f62589932eb9fcf0bf28fe95510bf614caf3169c67a85e75475a390a79b5ecc9",
						Memo:           "XgpzoumUqTDSDBVFFSH2wXOTgOZKcw24PgCel/P1spw=",
						TransactionXdr: "AAAAAC3/58Z9rycNLmF6voWX9VmDETFVGhFoWf66mcMuir/DAAAAZAAAAAAAAAAAAAAAAAAAAANeCnOi6ZSpMNIMFUUVIfbBc5OA5kpzDbg+AJ6X8/WynAAAAAEAAAAAAAAAAgAAAAFVU0QAAAAAAEbpO2riZmlZMkHuBxUBYAAas3hWyo9VL1IOdnfXAVFBAAAAADuaygAAAAAAGVL83DJFwH0sKmy6AIgJYD7GexiD0YuzSMioBCAUOJwAAAABVVNEAAAAAAAZUvzcMkXAfSwqbLoAiAlgPsZ7GIPRi7NIyKgEIBQ4nAAAAAAL68IAAAAAAgAAAAAAAAABRVVSAAAAAAALt4SwWfv1PIJvDRMenW0zu91YxZbphRFLA4O+gbAaigAAAAA=",
						Data:           params["data"][0],
					}

					mockEntityManager.On(
						"Persist",
						mock.AnythingOfType("*entities.AuthorizedTransaction"),
					).Run(func(args mock.Arguments) {
						value := args.Get(0).(*entities.AuthorizedTransaction)
						assert.Equal(t, authorizedTransaction.TransactionID, value.TransactionID)
						assert.Equal(t, authorizedTransaction.Memo, value.Memo)
						assert.Equal(t, authorizedTransaction.TransactionXdr, value.TransactionXdr)
						assert.WithinDuration(t, time.Now(), value.AuthorizedAt, 2*time.Second)
						assert.Equal(t, authorizedTransaction.Data, value.Data)
					}).Return(nil).Once()

					statusCode, response := net.GetResponse(testServer, params)
					responseString := strings.TrimSpace(string(response))
					assert.Equal(t, 200, statusCode)
					expected := test.StringToJSONMap(`{
  "info_status": "ok",
  "tx_status": "ok",
  "dest_info": "user data"
}`)
					assert.Equal(t, expected, test.StringToJSONMap(responseString))
				})

				Convey("when neither FI nor User is allowed it returns info_status = `denied`", func() {
					mockRepository.On(
						"GetAllowedFiByDomain",
						"stellar.org", // sender = `alice*stellar.org`
					).Return(
						nil,
						nil,
					).Once()

					mockRepository.On(
						"GetAllowedUserByDomainAndUserID",
						"stellar.org", // sender = `alice*stellar.org`
						"alice",
					).Return(
						nil,
						nil,
					).Once()

					statusCode, response := net.GetResponse(testServer, params)
					responseString := strings.TrimSpace(string(response))
					assert.Equal(t, 200, statusCode)
					expected := test.StringToJSONMap(`{
  "info_status": "denied",
  "tx_status": "ok"
}`)
					assert.Equal(t, expected, test.StringToJSONMap(responseString))
				})
			})
		})
	})
}
コード例 #2
0
func TestPaymentListener(t *testing.T) {
	mockEntityManager := new(mocks.MockEntityManager)
	mockHorizon := new(mocks.MockHorizon)
	mockRepository := new(mocks.MockRepository)

	var receiveHookStatusCode int

	receiveHookServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		assert.Equal(t, "POST", r.Method)
		assert.Equal(t, "1", r.FormValue("id"))
		assert.Equal(t, "GBIHSMPXC2KJ3NJVHEYTG3KCHYEUQRT45X6AWYWXMAXZOAX4F5LFZYYQ", r.FormValue("from"))
		assert.Equal(t, "200", r.FormValue("amount"))
		assert.Equal(t, "USD", r.FormValue("asset_code"))
		assert.Equal(t, "text", r.FormValue("memo_type"))
		assert.Equal(t, "testing", r.FormValue("memo"))
		w.WriteHeader(receiveHookStatusCode)
		fmt.Fprintln(w, "Response")
	}))
	defer receiveHookServer.Close()

	IssuingSeed := "SC34WILLHVADXMP6ACPMIRA6TRAWJMVCLPFNW7S6MUMXJVLAZUC4EWHP"
	ReceivingAccountId := "GATKP6ZQM5CSLECPMTAC5226PE367QALCPM6AFHTSULPPZMT62OOPMQB"

	config := &config.Config{
		Assets: []string{"USD", "EUR"},
		Accounts: &config.Accounts{
			// GD4I7AFSLZGTDL34TQLWJOM2NHLIIOEKD5RHHZUW54HERBLSIRKUOXRR
			IssuingSeed:        &IssuingSeed,
			ReceivingAccountId: &ReceivingAccountId,
		},
		Hooks: &config.Hooks{
			Receive: &receiveHookServer.URL,
		},
	}

	paymentListener, _ := NewPaymentListener(
		config,
		mockEntityManager,
		mockHorizon,
		mockRepository,
		mocks.Now,
	)

	Convey("PaymentListener", t, func() {
		operation := horizon.PaymentResponse{
			Id:          "1",
			From:        "GBIHSMPXC2KJ3NJVHEYTG3KCHYEUQRT45X6AWYWXMAXZOAX4F5LFZYYQ",
			PagingToken: "2",
			Amount:      "200",
		}

		mocks.PredefinedTime = time.Now()

		dbPayment := db.ReceivedPayment{
			OperationId: operation.Id,
			ProcessedAt: mocks.PredefinedTime,
			PagingToken: operation.PagingToken,
		}

		Convey("When operation is not a payment", func() {
			operation.Type = "create_account"
			dbPayment.Status = "Not a payment operation"
			mockEntityManager.On("Persist", &dbPayment).Return(nil).Once()

			Convey("it should save the status", func() {
				err := paymentListener.onPayment(operation)
				assert.Nil(t, err)
				mockEntityManager.AssertExpectations(t)
			})
		})

		Convey("When payment is sent not received", func() {
			operation.Type = "payment"
			operation.To = "GDNXBMIJLLLXZYKZBHXJ45WQ4AJQBRVT776YKGQTDBHTSPMNAFO3OZOS"
			dbPayment.Status = "Operation sent not received"
			mockEntityManager.On("Persist", &dbPayment).Return(nil).Once()

			Convey("it should save the status", func() {
				err := paymentListener.onPayment(operation)
				assert.Nil(t, err)
				mockEntityManager.AssertExpectations(t)
			})
		})

		Convey("When asset is not allowed (issuer)", func() {
			operation.Type = "payment"
			operation.To = "GATKP6ZQM5CSLECPMTAC5226PE367QALCPM6AFHTSULPPZMT62OOPMQB"
			operation.AssetCode = "USD"
			operation.AssetIssuer = "GC4WWLMUGZJMRVJM7JUVVZBY3LJ5HL4RKIPADEGKEMLAAJEDRONUGYG7"
			dbPayment.Status = "Asset not allowed"
			mockEntityManager.On("Persist", &dbPayment).Return(nil).Once()

			Convey("it should save the status", func() {
				err := paymentListener.onPayment(operation)
				assert.Nil(t, err)
				mockEntityManager.AssertExpectations(t)
			})
		})

		Convey("When asset is not allowed (code)", func() {
			operation.Type = "payment"
			operation.To = "GATKP6ZQM5CSLECPMTAC5226PE367QALCPM6AFHTSULPPZMT62OOPMQB"
			operation.AssetCode = "GBP"
			operation.AssetIssuer = "GD4I7AFSLZGTDL34TQLWJOM2NHLIIOEKD5RHHZUW54HERBLSIRKUOXRR"
			dbPayment.Status = "Asset not allowed"
			mockEntityManager.On("Persist", &dbPayment).Return(nil).Once()

			Convey("it should save the status", func() {
				err := paymentListener.onPayment(operation)
				assert.Nil(t, err)
				mockEntityManager.AssertExpectations(t)
			})
		})

		Convey("When transaction does not have memo", func() {
			operation.Type = "payment"
			operation.To = "GATKP6ZQM5CSLECPMTAC5226PE367QALCPM6AFHTSULPPZMT62OOPMQB"
			operation.AssetCode = "USD"
			operation.AssetIssuer = "GD4I7AFSLZGTDL34TQLWJOM2NHLIIOEKD5RHHZUW54HERBLSIRKUOXRR"
			dbPayment.Status = "Transaction does not have memo"

			mockHorizon.On("LoadMemo", &operation).Return(nil).Once()
			mockEntityManager.On("Persist", &dbPayment).Return(nil).Once()

			Convey("it should save the status", func() {
				err := paymentListener.onPayment(operation)
				assert.Nil(t, err)
				mockHorizon.AssertExpectations(t)
				mockEntityManager.AssertExpectations(t)
			})
		})

		Convey("When unable to load transaction memo", func() {
			operation.Type = "payment"
			operation.To = "GATKP6ZQM5CSLECPMTAC5226PE367QALCPM6AFHTSULPPZMT62OOPMQB"
			operation.AssetCode = "USD"
			operation.AssetIssuer = "GD4I7AFSLZGTDL34TQLWJOM2NHLIIOEKD5RHHZUW54HERBLSIRKUOXRR"

			mockHorizon.On("LoadMemo", &operation).Return(errors.New("Connection error")).Once()

			Convey("it should return error", func() {
				err := paymentListener.onPayment(operation)
				assert.Error(t, err)
				mockHorizon.AssertExpectations(t)
				mockEntityManager.AssertNotCalled(t, "Persist")
			})
		})

		Convey("When receive hook returns error", func() {
			operation.Type = "payment"
			operation.To = "GATKP6ZQM5CSLECPMTAC5226PE367QALCPM6AFHTSULPPZMT62OOPMQB"
			operation.AssetCode = "USD"
			operation.AssetIssuer = "GD4I7AFSLZGTDL34TQLWJOM2NHLIIOEKD5RHHZUW54HERBLSIRKUOXRR"
			operation.Memo.Type = "text"
			operation.Memo.Value = "testing"

			mockHorizon.On("LoadMemo", &operation).Return(nil).Once()
			receiveHookStatusCode = 503

			Convey("it should save the status", func() {
				err := paymentListener.onPayment(operation)
				assert.Error(t, err)
				mockHorizon.AssertExpectations(t)
				mockEntityManager.AssertNotCalled(t, "Persist")
			})
		})

		Convey("When receive hook returns success", func() {
			operation.Type = "payment"
			operation.To = "GATKP6ZQM5CSLECPMTAC5226PE367QALCPM6AFHTSULPPZMT62OOPMQB"
			operation.AssetCode = "USD"
			operation.AssetIssuer = "GD4I7AFSLZGTDL34TQLWJOM2NHLIIOEKD5RHHZUW54HERBLSIRKUOXRR"
			operation.Memo.Type = "text"
			operation.Memo.Value = "testing"

			dbPayment.Status = "Success"

			mockHorizon.On("LoadMemo", &operation).Return(nil).Once()
			mockEntityManager.On("Persist", &dbPayment).Return(nil).Once()
			receiveHookStatusCode = 200

			Convey("it should save the status", func() {
				err := paymentListener.onPayment(operation)
				assert.Nil(t, err)
				mockHorizon.AssertExpectations(t)
				mockEntityManager.AssertExpectations(t)
			})
		})
	})
}
コード例 #3
0
func TestPaymentListener(t *testing.T) {
	mockEntityManager := new(mocks.MockEntityManager)
	mockHorizon := new(mocks.MockHorizon)
	mockRepository := new(mocks.MockRepository)
	mockHTTPClient := new(mocks.MockHTTPClient)

	config := &config.Config{
		Assets: []config.Asset{
			{Code: "USD", Issuer: "GD4I7AFSLZGTDL34TQLWJOM2NHLIIOEKD5RHHZUW54HERBLSIRKUOXRR"},
			{Code: "EUR", Issuer: "GD4I7AFSLZGTDL34TQLWJOM2NHLIIOEKD5RHHZUW54HERBLSIRKUOXRR"},
		},
		Accounts: config.Accounts{
			IssuingAccountID:   "GATKP6ZQM5CSLECPMTAC5226PE367QALCPM6AFHTSULPPZMT62OOPMQB",
			ReceivingAccountID: "GATKP6ZQM5CSLECPMTAC5226PE367QALCPM6AFHTSULPPZMT62OOPMQB",
		},
		Callbacks: config.Callbacks{
			Receive: "http://receive_callback",
		},
	}

	paymentListener, _ := NewPaymentListener(
		config,
		mockEntityManager,
		mockHorizon,
		mockRepository,
		mocks.Now,
	)

	paymentListener.client = mockHTTPClient

	Convey("PaymentListener", t, func() {
		operation := horizon.PaymentResponse{
			ID:          "1",
			From:        "GBIHSMPXC2KJ3NJVHEYTG3KCHYEUQRT45X6AWYWXMAXZOAX4F5LFZYYQ",
			PagingToken: "2",
			Amount:      "200",
		}

		mocks.PredefinedTime = time.Now()

		dbPayment := entities.ReceivedPayment{
			OperationID: operation.ID,
			ProcessedAt: mocks.PredefinedTime,
			PagingToken: operation.PagingToken,
		}

		Convey("When operation exists", func() {
			operation.Type = "payment"
			mockRepository.On("GetReceivedPaymentByID", int64(1)).Return(&entities.ReceivedPayment{}, nil).Once()

			Convey("it should save the status", func() {
				err := paymentListener.onPayment(operation)
				assert.Nil(t, err)
				mockEntityManager.AssertExpectations(t)
			})
		})

		Convey("When operation is not a payment", func() {
			operation.Type = "create_account"
			dbPayment.Status = "Not a payment operation"
			mockEntityManager.On("Persist", &dbPayment).Return(nil).Once()
			mockRepository.On("GetReceivedPaymentByID", int64(1)).Return(nil, nil).Once()

			Convey("it should save the status", func() {
				err := paymentListener.onPayment(operation)
				assert.Nil(t, err)
				mockEntityManager.AssertExpectations(t)
			})
		})

		Convey("When payment is sent not received", func() {
			operation.Type = "payment"
			operation.To = "GDNXBMIJLLLXZYKZBHXJ45WQ4AJQBRVT776YKGQTDBHTSPMNAFO3OZOS"
			dbPayment.Status = "Operation sent not received"
			mockEntityManager.On("Persist", &dbPayment).Return(nil).Once()
			mockRepository.On("GetReceivedPaymentByID", int64(1)).Return(nil, nil).Once()

			Convey("it should save the status", func() {
				err := paymentListener.onPayment(operation)
				assert.Nil(t, err)
				mockEntityManager.AssertExpectations(t)
			})
		})

		Convey("When asset is not allowed (issuer)", func() {
			operation.Type = "payment"
			operation.To = "GATKP6ZQM5CSLECPMTAC5226PE367QALCPM6AFHTSULPPZMT62OOPMQB"
			operation.AssetCode = "USD"
			operation.AssetIssuer = "GC4WWLMUGZJMRVJM7JUVVZBY3LJ5HL4RKIPADEGKEMLAAJEDRONUGYG7"
			dbPayment.Status = "Asset not allowed"
			mockEntityManager.On("Persist", &dbPayment).Return(nil).Once()
			mockRepository.On("GetReceivedPaymentByID", int64(1)).Return(nil, nil).Once()

			Convey("it should save the status", func() {
				err := paymentListener.onPayment(operation)
				assert.Nil(t, err)
				mockEntityManager.AssertExpectations(t)
			})
		})

		Convey("When asset is not allowed (code)", func() {
			operation.Type = "payment"
			operation.To = "GATKP6ZQM5CSLECPMTAC5226PE367QALCPM6AFHTSULPPZMT62OOPMQB"
			operation.AssetCode = "GBP"
			operation.AssetIssuer = "GD4I7AFSLZGTDL34TQLWJOM2NHLIIOEKD5RHHZUW54HERBLSIRKUOXRR"
			dbPayment.Status = "Asset not allowed"
			mockEntityManager.On("Persist", &dbPayment).Return(nil).Once()
			mockRepository.On("GetReceivedPaymentByID", int64(1)).Return(nil, nil).Once()

			Convey("it should save the status", func() {
				err := paymentListener.onPayment(operation)
				assert.Nil(t, err)
				mockEntityManager.AssertExpectations(t)
			})
		})

		Convey("When unable to load transaction memo", func() {
			operation.Type = "payment"
			operation.To = "GATKP6ZQM5CSLECPMTAC5226PE367QALCPM6AFHTSULPPZMT62OOPMQB"
			operation.AssetCode = "USD"
			operation.AssetIssuer = "GD4I7AFSLZGTDL34TQLWJOM2NHLIIOEKD5RHHZUW54HERBLSIRKUOXRR"

			mockRepository.On("GetReceivedPaymentByID", int64(1)).Return(nil, nil).Once()
			mockHorizon.On("LoadMemo", &operation).Return(errors.New("Connection error")).Once()

			Convey("it should return error", func() {
				err := paymentListener.onPayment(operation)
				assert.Error(t, err)
				mockHorizon.AssertExpectations(t)
				mockEntityManager.AssertNotCalled(t, "Persist")
			})
		})

		Convey("When receive callback returns error", func() {
			operation.Type = "payment"
			operation.To = "GATKP6ZQM5CSLECPMTAC5226PE367QALCPM6AFHTSULPPZMT62OOPMQB"
			operation.AssetCode = "USD"
			operation.AssetIssuer = "GD4I7AFSLZGTDL34TQLWJOM2NHLIIOEKD5RHHZUW54HERBLSIRKUOXRR"
			operation.Memo.Type = "text"
			operation.Memo.Value = "testing"

			mockRepository.On("GetReceivedPaymentByID", int64(1)).Return(nil, nil).Once()
			mockHorizon.On("LoadMemo", &operation).Return(nil).Once()

			mockHTTPClient.On(
				"PostForm",
				"http://receive_callback",
				url.Values{
					"id":         {"1"},
					"from":       {"GBIHSMPXC2KJ3NJVHEYTG3KCHYEUQRT45X6AWYWXMAXZOAX4F5LFZYYQ"},
					"amount":     {"200"},
					"asset_code": {"USD"},
					"memo_type":  {"text"},
					"memo":       {"testing"},
					"data":       {""},
				},
			).Return(
				net.BuildHTTPResponse(503, "ok"),
				nil,
			).Once()

			Convey("it should save the status", func() {
				err := paymentListener.onPayment(operation)
				assert.Error(t, err)
				mockHorizon.AssertExpectations(t)
				mockEntityManager.AssertNotCalled(t, "Persist")
			})
		})

		Convey("When receive callback returns success", func() {
			operation.Type = "payment"
			operation.To = "GATKP6ZQM5CSLECPMTAC5226PE367QALCPM6AFHTSULPPZMT62OOPMQB"
			operation.AssetCode = "USD"
			operation.AssetIssuer = "GD4I7AFSLZGTDL34TQLWJOM2NHLIIOEKD5RHHZUW54HERBLSIRKUOXRR"
			operation.Memo.Type = "text"
			operation.Memo.Value = "testing"

			dbPayment.Status = "Success"

			mockRepository.On("GetReceivedPaymentByID", int64(1)).Return(nil, nil).Once()
			mockHorizon.On("LoadMemo", &operation).Return(nil).Once()
			mockEntityManager.On("Persist", &dbPayment).Return(nil).Once()

			mockHTTPClient.On(
				"PostForm",
				"http://receive_callback",
				url.Values{
					"id":         {"1"},
					"from":       {"GBIHSMPXC2KJ3NJVHEYTG3KCHYEUQRT45X6AWYWXMAXZOAX4F5LFZYYQ"},
					"amount":     {"200"},
					"asset_code": {"USD"},
					"memo_type":  {"text"},
					"memo":       {"testing"},
					"data":       {""},
				},
			).Return(
				net.BuildHTTPResponse(200, "ok"),
				nil,
			).Once()

			Convey("it should save the status", func() {
				err := paymentListener.onPayment(operation)
				assert.Nil(t, err)
				mockHorizon.AssertExpectations(t)
				mockEntityManager.AssertExpectations(t)
			})
		})

		Convey("When receive callback returns success (no memo)", func() {
			operation.Type = "payment"
			operation.To = "GATKP6ZQM5CSLECPMTAC5226PE367QALCPM6AFHTSULPPZMT62OOPMQB"
			operation.AssetCode = "USD"
			operation.AssetIssuer = "GD4I7AFSLZGTDL34TQLWJOM2NHLIIOEKD5RHHZUW54HERBLSIRKUOXRR"

			dbPayment.Status = "Success"

			mockRepository.On("GetReceivedPaymentByID", int64(1)).Return(nil, nil).Once()
			mockHorizon.On("LoadMemo", &operation).Return(nil).Once()
			mockEntityManager.On("Persist", &dbPayment).Return(nil).Once()

			mockHTTPClient.On(
				"PostForm",
				"http://receive_callback",
				url.Values{
					"id":         {"1"},
					"from":       {"GBIHSMPXC2KJ3NJVHEYTG3KCHYEUQRT45X6AWYWXMAXZOAX4F5LFZYYQ"},
					"amount":     {"200"},
					"asset_code": {"USD"},
					"memo_type":  {""},
					"memo":       {""},
					"data":       {""},
				},
			).Return(
				net.BuildHTTPResponse(200, "ok"),
				nil,
			).Once()

			Convey("it should save the status", func() {
				err := paymentListener.onPayment(operation)
				assert.Nil(t, err)
				mockHorizon.AssertExpectations(t)
				mockEntityManager.AssertExpectations(t)
			})
		})

		Convey("When receive callback returns success and compliance server is connected", func() {
			paymentListener.config.Compliance = "http://compliance"

			operation.Type = "payment"
			operation.To = "GATKP6ZQM5CSLECPMTAC5226PE367QALCPM6AFHTSULPPZMT62OOPMQB"
			operation.AssetCode = "USD"
			operation.AssetIssuer = "GD4I7AFSLZGTDL34TQLWJOM2NHLIIOEKD5RHHZUW54HERBLSIRKUOXRR"
			operation.Memo.Type = "hash"
			operation.Memo.Value = "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"

			dbPayment.Status = "Success"

			mockRepository.On("GetReceivedPaymentByID", int64(1)).Return(nil, nil).Once()
			mockHorizon.On("LoadMemo", &operation).Return(nil).Once()
			mockEntityManager.On("Persist", &dbPayment).Return(nil).Once()

			mockHTTPClient.On(
				"PostForm",
				"http://compliance/receive",
				url.Values{"memo": {"b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"}},
			).Return(
				net.BuildHTTPResponse(200, "{\"data\": \"hello world\"}"),
				nil,
			).Once()

			mockHTTPClient.On(
				"PostForm",
				"http://receive_callback",
				url.Values{
					"id":         {"1"},
					"from":       {"GBIHSMPXC2KJ3NJVHEYTG3KCHYEUQRT45X6AWYWXMAXZOAX4F5LFZYYQ"},
					"amount":     {"200"},
					"asset_code": {"USD"},
					"memo_type":  {"hash"},
					"memo":       {"b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"},
					"data":       {"hello world"},
				},
			).Return(
				net.BuildHTTPResponse(200, "ok"),
				nil,
			).Once()

			Convey("it should save the status", func() {
				err := paymentListener.onPayment(operation)
				assert.Nil(t, err)
				mockHorizon.AssertExpectations(t)
				mockEntityManager.AssertExpectations(t)
			})
		})
	})
}
コード例 #4
0
func TestTransactionSubmitter(t *testing.T) {
	mockHorizon := new(mocks.MockHorizon)
	mockEntityManager := new(mocks.MockEntityManager)
	mocks.PredefinedTime = time.Now()

	Convey("TransactionSubmitter", t, func() {
		seed := "SDZT3EJZ7FZRYNTLOZ7VH6G5UYBFO2IO3Q5PGONMILPCZU3AL7QNZHTE"
		accountID := "GCLOMB72ODBFUGK4E2BK7VMR3RNZ5WSTMEOGNA2YUVHFR3WMH2XBAB6H"

		Convey("LoadAccount", func() {
			transactionSubmitter := NewTransactionSubmitter(
				mockHorizon,
				mockEntityManager,
				"Test SDF Network ; September 2015",
				mocks.Now,
			)

			Convey("When seed is invalid", func() {
				_, err := transactionSubmitter.LoadAccount("invalidSeed")
				assert.NotNil(t, err)
			})

			Convey("When there is a problem loading an account", func() {
				mockHorizon.On(
					"LoadAccount",
					accountID,
				).Return(
					horizon.AccountResponse{},
					errors.New("Account not found"),
				).Once()

				_, err := transactionSubmitter.LoadAccount(seed)
				assert.NotNil(t, err)
				mockHorizon.AssertExpectations(t)
			})

			Convey("Successfully loads an account", func() {
				mockHorizon.On(
					"LoadAccount",
					accountID,
				).Return(
					horizon.AccountResponse{
						AccountID:      accountID,
						SequenceNumber: "10372672437354496",
					},
					nil,
				).Once()

				account, err := transactionSubmitter.LoadAccount(seed)
				assert.Nil(t, err)
				assert.Equal(t, account.Keypair.Address(), accountID)
				assert.Equal(t, account.Seed, seed)
				assert.Equal(t, account.SequenceNumber, uint64(10372672437354496))
				mockHorizon.AssertExpectations(t)
			})
		})

		Convey("SubmitTransaction", func() {
			Convey("Submits transaction without a memo", func() {
				operation := b.Payment(
					b.Destination{"GB3W7VQ2A2IOQIS4LUFUMRC2DWXONUDH24ROLE6RS4NGUNHVSXKCABOM"},
					b.NativeAmount{"100"},
				)

				Convey("Error response from horizon", func() {
					transactionSubmitter := NewTransactionSubmitter(
						mockHorizon,
						mockEntityManager,
						"Test SDF Network ; September 2015",
						mocks.Now,
					)

					mockHorizon.On(
						"LoadAccount",
						accountID,
					).Return(
						horizon.AccountResponse{
							AccountID:      accountID,
							SequenceNumber: "10372672437354496",
						},
						nil,
					).Once()

					err := transactionSubmitter.InitAccount(seed)
					assert.Nil(t, err)

					txB64 := "AAAAAJbmB/pwwloZXCaCr9WR3Fue2lNhHGaDWKVOWO7MPq4QAAAAZAAk2eQAAAABAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAAd2/WGgaQ6CJcXQtGRFodrubQZ9ci5ZPRlxpqNPWV1CAAAAAAAAAAADuaygAAAAAAAAAAAcw+rhAAAABAyFjIMIZOtstCWtZlVBDj1AhTmsk5v1i2GGY4by2b5mgZoXXGgFTB8sfbQav0LzFKCcxY8h+9xPMT2e9xznAfDw=="

					// Persist sending transaction
					mockEntityManager.On(
						"Persist",
						mock.AnythingOfType("*entities.SentTransaction"),
					).Return(nil).Once().Run(func(args mock.Arguments) {
						transaction := args.Get(0).(*entities.SentTransaction)
						assert.Equal(t, "4f885999be6ea7891052a53e496bcfb5c5a1a5bfb31923f649b028fdc74dd050", transaction.TransactionID)
						assert.Equal(t, "sending", string(transaction.Status))
						assert.Equal(t, "GCLOMB72ODBFUGK4E2BK7VMR3RNZ5WSTMEOGNA2YUVHFR3WMH2XBAB6H", transaction.Source)
						assert.Equal(t, mocks.PredefinedTime, transaction.SubmittedAt)
						assert.Equal(t, txB64, transaction.EnvelopeXdr)
					})

					// Persist failure
					mockEntityManager.On(
						"Persist",
						mock.AnythingOfType("*entities.SentTransaction"),
					).Return(nil).Once().Run(func(args mock.Arguments) {
						transaction := args.Get(0).(*entities.SentTransaction)
						assert.Equal(t, "4f885999be6ea7891052a53e496bcfb5c5a1a5bfb31923f649b028fdc74dd050", transaction.TransactionID)
						assert.Equal(t, "failure", string(transaction.Status))
						assert.Equal(t, "GCLOMB72ODBFUGK4E2BK7VMR3RNZ5WSTMEOGNA2YUVHFR3WMH2XBAB6H", transaction.Source)
						assert.Equal(t, mocks.PredefinedTime, transaction.SubmittedAt)
						assert.Equal(t, txB64, transaction.EnvelopeXdr)
					})

					mockHorizon.On("SubmitTransaction", txB64).Return(
						horizon.SubmitTransactionResponse{
							Ledger: nil,
							Extras: &horizon.SubmitTransactionResponseExtras{
								ResultXdr: "AAAAAAAAAGT/////AAAAAQAAAAAAAAAB////+wAAAAA=", // no_destination
							},
						},
						nil,
					).Once()

					_, err = transactionSubmitter.SubmitTransaction(seed, operation, nil)
					assert.Nil(t, err)
					mockHorizon.AssertExpectations(t)
				})

				Convey("Bad Sequence response from horizon", func() {
					transactionSubmitter := NewTransactionSubmitter(
						mockHorizon,
						mockEntityManager,
						"Test SDF Network ; September 2015",
						mocks.Now,
					)

					mockHorizon.On(
						"LoadAccount",
						accountID,
					).Return(
						horizon.AccountResponse{
							AccountID:      accountID,
							SequenceNumber: "10372672437354496",
						},
						nil,
					).Once()

					err := transactionSubmitter.InitAccount(seed)
					assert.Nil(t, err)

					txB64 := "AAAAAJbmB/pwwloZXCaCr9WR3Fue2lNhHGaDWKVOWO7MPq4QAAAAZAAk2eQAAAABAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAAd2/WGgaQ6CJcXQtGRFodrubQZ9ci5ZPRlxpqNPWV1CAAAAAAAAAAADuaygAAAAAAAAAAAcw+rhAAAABAyFjIMIZOtstCWtZlVBDj1AhTmsk5v1i2GGY4by2b5mgZoXXGgFTB8sfbQav0LzFKCcxY8h+9xPMT2e9xznAfDw=="

					// Persist sending transaction
					mockEntityManager.On(
						"Persist",
						mock.AnythingOfType("*entities.SentTransaction"),
					).Return(nil).Once().Run(func(args mock.Arguments) {
						transaction := args.Get(0).(*entities.SentTransaction)
						assert.Equal(t, "4f885999be6ea7891052a53e496bcfb5c5a1a5bfb31923f649b028fdc74dd050", transaction.TransactionID)
						assert.Equal(t, "sending", string(transaction.Status))
						assert.Equal(t, "GCLOMB72ODBFUGK4E2BK7VMR3RNZ5WSTMEOGNA2YUVHFR3WMH2XBAB6H", transaction.Source)
						assert.Equal(t, mocks.PredefinedTime, transaction.SubmittedAt)
						assert.Equal(t, txB64, transaction.EnvelopeXdr)
					})

					// Persist failure
					mockEntityManager.On(
						"Persist",
						mock.AnythingOfType("*entities.SentTransaction"),
					).Return(nil).Once().Run(func(args mock.Arguments) {
						transaction := args.Get(0).(*entities.SentTransaction)
						assert.Equal(t, "4f885999be6ea7891052a53e496bcfb5c5a1a5bfb31923f649b028fdc74dd050", transaction.TransactionID)
						assert.Equal(t, "failure", string(transaction.Status))
						assert.Equal(t, "GCLOMB72ODBFUGK4E2BK7VMR3RNZ5WSTMEOGNA2YUVHFR3WMH2XBAB6H", transaction.Source)
						assert.Equal(t, mocks.PredefinedTime, transaction.SubmittedAt)
						assert.Equal(t, txB64, transaction.EnvelopeXdr)
					})

					mockHorizon.On("SubmitTransaction", txB64).Return(
						horizon.SubmitTransactionResponse{
							Ledger: nil,
							Extras: &horizon.SubmitTransactionResponseExtras{
								ResultXdr: "AAAAAAAAAAD////7AAAAAA==", // tx_bad_seq
							},
						},
						nil,
					).Once()

					// Updating sequence number
					mockHorizon.On(
						"LoadAccount",
						accountID,
					).Return(
						horizon.AccountResponse{
							AccountID:      accountID,
							SequenceNumber: "100",
						},
						nil,
					).Once()

					_, err = transactionSubmitter.SubmitTransaction(seed, operation, nil)
					assert.Nil(t, err)
					assert.Equal(t, uint64(100), transactionSubmitter.Accounts[seed].SequenceNumber)
					mockHorizon.AssertExpectations(t)
				})

				Convey("Successfully submits a transaction", func() {
					transactionSubmitter := NewTransactionSubmitter(
						mockHorizon,
						mockEntityManager,
						"Test SDF Network ; September 2015",
						mocks.Now,
					)

					mockHorizon.On(
						"LoadAccount",
						accountID,
					).Return(
						horizon.AccountResponse{
							AccountID:      accountID,
							SequenceNumber: "10372672437354496",
						},
						nil,
					).Once()

					err := transactionSubmitter.InitAccount(seed)
					assert.Nil(t, err)

					txB64 := "AAAAAJbmB/pwwloZXCaCr9WR3Fue2lNhHGaDWKVOWO7MPq4QAAAAZAAk2eQAAAABAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAAd2/WGgaQ6CJcXQtGRFodrubQZ9ci5ZPRlxpqNPWV1CAAAAAAAAAAADuaygAAAAAAAAAAAcw+rhAAAABAyFjIMIZOtstCWtZlVBDj1AhTmsk5v1i2GGY4by2b5mgZoXXGgFTB8sfbQav0LzFKCcxY8h+9xPMT2e9xznAfDw=="

					// Persist sending transaction
					mockEntityManager.On(
						"Persist",
						mock.AnythingOfType("*entities.SentTransaction"),
					).Return(nil).Once().Run(func(args mock.Arguments) {
						transaction := args.Get(0).(*entities.SentTransaction)
						assert.Equal(t, "4f885999be6ea7891052a53e496bcfb5c5a1a5bfb31923f649b028fdc74dd050", transaction.TransactionID)
						assert.Equal(t, "sending", string(transaction.Status))
						assert.Equal(t, "GCLOMB72ODBFUGK4E2BK7VMR3RNZ5WSTMEOGNA2YUVHFR3WMH2XBAB6H", transaction.Source)
						assert.Equal(t, mocks.PredefinedTime, transaction.SubmittedAt)
						assert.Equal(t, txB64, transaction.EnvelopeXdr)
					})

					// Persist failure
					mockEntityManager.On(
						"Persist",
						mock.AnythingOfType("*entities.SentTransaction"),
					).Return(nil).Once().Run(func(args mock.Arguments) {
						transaction := args.Get(0).(*entities.SentTransaction)
						assert.Equal(t, "4f885999be6ea7891052a53e496bcfb5c5a1a5bfb31923f649b028fdc74dd050", transaction.TransactionID)
						assert.Equal(t, "success", string(transaction.Status))
						assert.Equal(t, "GCLOMB72ODBFUGK4E2BK7VMR3RNZ5WSTMEOGNA2YUVHFR3WMH2XBAB6H", transaction.Source)
						assert.Equal(t, mocks.PredefinedTime, transaction.SubmittedAt)
						assert.Equal(t, txB64, transaction.EnvelopeXdr)
					})

					ledger := uint64(1486276)
					mockHorizon.On("SubmitTransaction", txB64).Return(
						horizon.SubmitTransactionResponse{Ledger: &ledger},
						nil,
					).Once()

					response, err := transactionSubmitter.SubmitTransaction(seed, operation, nil)
					assert.Nil(t, err)
					assert.Equal(t, *response.Ledger, ledger)
					assert.Equal(t, uint64(10372672437354497), transactionSubmitter.Accounts[seed].SequenceNumber)
					mockHorizon.AssertExpectations(t)
				})
			})

			Convey("Submits transaction with a memo", func() {
				operation := b.Payment(
					b.Destination{"GB3W7VQ2A2IOQIS4LUFUMRC2DWXONUDH24ROLE6RS4NGUNHVSXKCABOM"},
					b.NativeAmount{"100"},
				)

				memo := b.MemoText{"Testing!"}

				Convey("Successfully submits a transaction", func() {
					transactionSubmitter := NewTransactionSubmitter(
						mockHorizon,
						mockEntityManager,
						"Test SDF Network ; September 2015",
						mocks.Now,
					)

					mockHorizon.On(
						"LoadAccount",
						accountID,
					).Return(
						horizon.AccountResponse{
							AccountID:      accountID,
							SequenceNumber: "10372672437354496",
						},
						nil,
					).Once()

					err := transactionSubmitter.InitAccount(seed)
					assert.Nil(t, err)

					txB64 := "AAAAAJbmB/pwwloZXCaCr9WR3Fue2lNhHGaDWKVOWO7MPq4QAAAAZAAk2eQAAAABAAAAAAAAAAEAAAAIVGVzdGluZyEAAAABAAAAAAAAAAEAAAAAd2/WGgaQ6CJcXQtGRFodrubQZ9ci5ZPRlxpqNPWV1CAAAAAAAAAAADuaygAAAAAAAAAAAcw+rhAAAABAU5ahFsd28sVKSUFcmAiEf+zSLXhf9HG/pJuQirR0s43zs7Y43vM8T3sIvJWHgwMADaZiy/D+evYWd/vS/uO8Ag=="

					// Persist sending transaction
					mockEntityManager.On(
						"Persist",
						mock.AnythingOfType("*entities.SentTransaction"),
					).Return(nil).Once().Run(func(args mock.Arguments) {
						transaction := args.Get(0).(*entities.SentTransaction)
						assert.Equal(t, "60cb3c020b0c97352cbabdf68a822b04baea61927b0f1ac31260a9f8d0150316", transaction.TransactionID)
						assert.Equal(t, "sending", string(transaction.Status))
						assert.Equal(t, "GCLOMB72ODBFUGK4E2BK7VMR3RNZ5WSTMEOGNA2YUVHFR3WMH2XBAB6H", transaction.Source)
						assert.Equal(t, mocks.PredefinedTime, transaction.SubmittedAt)
						assert.Equal(t, txB64, transaction.EnvelopeXdr)
					})

					// Persist failure
					mockEntityManager.On(
						"Persist",
						mock.AnythingOfType("*entities.SentTransaction"),
					).Return(nil).Once().Run(func(args mock.Arguments) {
						transaction := args.Get(0).(*entities.SentTransaction)
						assert.Equal(t, "60cb3c020b0c97352cbabdf68a822b04baea61927b0f1ac31260a9f8d0150316", transaction.TransactionID)
						assert.Equal(t, "success", string(transaction.Status))
						assert.Equal(t, "GCLOMB72ODBFUGK4E2BK7VMR3RNZ5WSTMEOGNA2YUVHFR3WMH2XBAB6H", transaction.Source)
						assert.Equal(t, mocks.PredefinedTime, transaction.SubmittedAt)
						assert.Equal(t, txB64, transaction.EnvelopeXdr)
					})

					ledger := uint64(1486276)
					mockHorizon.On("SubmitTransaction", txB64).Return(
						horizon.SubmitTransactionResponse{Ledger: &ledger},
						nil,
					).Once()

					response, err := transactionSubmitter.SubmitTransaction(seed, operation, memo)
					assert.Nil(t, err)
					assert.Equal(t, *response.Ledger, ledger)
					assert.Equal(t, uint64(10372672437354497), transactionSubmitter.Accounts[seed].SequenceNumber)
					mockHorizon.AssertExpectations(t)
				})
			})
		})
	})
}