Skip to content

Commit

Permalink
Merge pull request #4 from bot-api/fix-mock
Browse files Browse the repository at this point in the history
Fix go 1.7 tests, Add session middleware and DownloadFile method for api
  • Loading branch information
m0sth8 committed Jul 21, 2016
2 parents fc8ddae + 7a6f92e commit 2cc6390
Show file tree
Hide file tree
Showing 10 changed files with 299 additions and 14 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ prepare: fmt vet checkfmt errcheck test race lint

test_v:
@echo "$(OK_COLOR)Test packages$(NO_COLOR)"
@go test -cover -v ./...
@go test -timeout 1m -cover -v ./...

test:
@echo "$(OK_COLOR)Test packages$(NO_COLOR)"
@go test -cover ./...
@go test -timeout 10s -cover ./...

test_i:
ifdef API_BOT_TOKEN
Expand Down
41 changes: 34 additions & 7 deletions api.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import (
"strings"

"golang.org/x/net/context"
"golang.org/x/net/context/ctxhttp"
)

const (
Expand Down Expand Up @@ -264,6 +263,38 @@ func (c *API) GetFile(ctx context.Context, cfg FileCfg) (*File, error) {
return &file, nil
}

// DownloadFile downloads file from telegram servers to w
//
// Requires FileID
func (c *API) DownloadFile(ctx context.Context, cfg FileCfg, w io.Writer) error {
f, err := c.GetFile(ctx, cfg)
if err != nil {
return err
}
req, err := http.NewRequest("GET", f.Link, nil)
if err != nil {
return err
}
resp, err := c.client.Do(req)
if err != nil {
return err
}
defer func() {
if err := resp.Body.Close(); err != nil {
if c.debug {
c.print("body close error", map[string]interface{}{
"error": err.Error(),
})
}
}
}()
_, err = io.Copy(w, resp.Body)
if err != nil {
return err
}
return nil
}

// AnswerCallbackQuery sends a response to an inline query callback.
func (c *API) AnswerCallbackQuery(
ctx context.Context,
Expand Down Expand Up @@ -536,12 +567,8 @@ func (c *API) makeRequest(
var err error
var resp *http.Response

if httpClient, ok := c.client.(*http.Client); ok {
resp, err = ctxhttp.Do(ctx, httpClient, req)
} else {
// TODO: implement cancel logic for non http.Client
resp, err = c.client.Do(req)
}
resp, err = makeRequest(ctx, c.client, req)

if err != nil {
return err
}
Expand Down
42 changes: 42 additions & 0 deletions api_17.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// +build go1.7

package telegram

import (
"net/http"
"net/url"

"golang.org/x/net/context"
)

// these errors are from net/http for go 1.7
const (
errRequestCanceled = "net/http: request canceled"
errRequestCanceledConn = "net/http: request canceled while waiting for connection"
)

func makeRequest(ctx context.Context, client HTTPDoer, req *http.Request) (*http.Response, error) {
var (
resp *http.Response
err error
)
if httpClient, ok := client.(*http.Client); ok {
resp, err = httpClient.Do(req.WithContext(ctx))
} else {
// TODO: implement cancel logic for non http.Client
resp, err = client.Do(req)
}
if err != nil {
if urlErr, casted := err.(*url.Error); casted {
if urlErr.Err == context.Canceled {
return resp, context.Canceled
}
errMsg := urlErr.Err.Error()
if errMsg == errRequestCanceled ||
errMsg == errRequestCanceledConn {
return resp, context.Canceled
}
}
}
return resp, err
}
2 changes: 1 addition & 1 deletion api_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ func TestApi_makeRequest_testContextCancel(t *testing.T) {
cancelReq()
// receive error from makeRequest
err := <-errCh
assert.Equal(t, context.Canceled, err)
assert.Equal(t, context.Canceled, err, "Actual err %v", err)

}

Expand Down
24 changes: 24 additions & 0 deletions api_pre_17.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// +build !go1.7

package telegram

import (
"net/http"

"golang.org/x/net/context"
"golang.org/x/net/context/ctxhttp"
)

func makeRequest(ctx context.Context, client HTTPDoer, req *http.Request) (*http.Response, error) {
var (
resp *http.Response
err error
)
if httpClient, ok := client.(*http.Client); ok {
resp, err = ctxhttp.Do(ctx, httpClient, req)
} else {
// TODO: implement cancel logic for non http.Client
resp, err = client.Do(req)
}
return resp, err
}
42 changes: 41 additions & 1 deletion api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@
package telegram_test

import (
"bytes"
"net/http"
"testing"
"time"

"github.com/bot-api/telegram"
"github.com/jarcoal/httpmock"
"github.com/m0sth8/httpmock"
"golang.org/x/net/context"
"gopkg.in/stretchr/testify.v1/assert"
"gopkg.in/stretchr/testify.v1/require"
)

var apiToken = "token"
Expand Down Expand Up @@ -284,3 +286,41 @@ func TestAPI_GetUserProfilePhotos(t *testing.T) {

}
}

func TestAPI_DownloadFile(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*1)
defer cancel()
httpmock.Activate()
defer httpmock.DeactivateAndReset()
api := telegram.New(apiToken)

httpmock.RegisterResponder(
"POST",
"https://api.telegram.org/bottoken/getFile",
httpmock.NewStringResponder(200, `
{
"ok": true,
"result":
{
"file_path": "path_to_file",
"file_id": "file_id"
}
}`,
),
)
httpmock.RegisterResponder(
"GET",
"https://api.telegram.org/file/bottoken/path_to_file",
httpmock.NewStringResponder(200, "FILE DATA"),
)
f, err := api.GetFile(ctx, telegram.FileCfg{FileID: "file_id"})
require.NoError(t, err)
assert.Equal(t, "file_id", f.FileID)
assert.Equal(t, "https://api.telegram.org/file/bottoken/path_to_file", f.Link)

buf := bytes.NewBuffer(nil)
err = api.DownloadFile(ctx, telegram.FileCfg{FileID: "file_id"}, buf)
require.NoError(t, err)
assert.Equal(t, "FILE DATA", buf.String())

}
6 changes: 4 additions & 2 deletions telebot/bot_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"time"

"github.com/bot-api/telegram"
"github.com/jarcoal/httpmock"
"github.com/m0sth8/httpmock"
"golang.org/x/net/context"
"gopkg.in/stretchr/testify.v1/assert"
"gopkg.in/stretchr/testify.v1/require"
Expand Down Expand Up @@ -285,7 +285,9 @@ func TestBot_Serve(t *testing.T) {
cancel()
select {
case err := <-errCh:
require.Equal(t, err, context.Canceled)
require.Equal(t, err, context.Canceled, "exp %v", err.Error())
case <-time.After(time.Second * 5):
t.Fatal("Server should be cancelled")
}
}

Expand Down
54 changes: 54 additions & 0 deletions telebot/middleware.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package telebot

import (
"strings"

"golang.org/x/net/context"
)

Expand All @@ -14,6 +16,11 @@ type Commander interface {
Command(ctx context.Context, arg string) error
}

// InlineCallback interface describes inline callback function.
type InlineCallback interface {
Callback(ctx context.Context, data string) error
}

type (

// MiddlewareFunc defines a function to process middleware.
Expand All @@ -31,6 +38,10 @@ type (
// CommandFunc defines a function to handle commands.
// Implements Commander interface.
CommandFunc func(ctx context.Context, arg string) error

// CallbackFunc defines a function to handle callbacks.
// Implements InlineCallback interface.
CallbackFunc func(ctx context.Context, data string) error
)

// Handle method handles message update.
Expand All @@ -43,6 +54,11 @@ func (c CommandFunc) Command(ctx context.Context, arg string) error {
return c(ctx, arg)
}

// Callback method handles command on message update.
func (c CallbackFunc) Callback(ctx context.Context, data string) error {
return c(ctx, data)
}

// Commands middleware takes map of commands.
// It runs associated Commander if update messages has a command message.
// Empty command (e.x. "": Commander) used as a default Commander.
Expand Down Expand Up @@ -72,3 +88,41 @@ func Commands(commands map[string]Commander) MiddlewareFunc {
})
}
}

// Callbacks middleware takes map of callbacks.
// It runs associated InlineCallback if update messages has a callback query.
// Callback path is divided by ":".
// Empty callback (e.x. "": InlineCallback) used as a default callback handler.
// Nil callback (e.x. "smth": nil) used as an EmptyHandler
// Take a look on examples/callbacks/main.go to know more.
func Callbacks(callbacks map[string]InlineCallback) MiddlewareFunc {
return func(next Handler) Handler {
return HandlerFunc(func(ctx context.Context) error {
update := GetUpdate(ctx)
if update.CallbackQuery == nil {
return next.Handle(ctx)
}
queryData := strings.SplitN(update.CallbackQuery.Data, ":", 2)
var prefix, data string
switch len(queryData) {
case 2:
data = queryData[1]
fallthrough
case 1:
prefix = queryData[0]
default:
return next.Handle(ctx)
}
callback, ok := callbacks[prefix]
if !ok {
if callback = callbacks[""]; callback == nil {
return next.Handle(ctx)
}
}
if callback == nil {
return next.Handle(ctx)
}
return callback.Callback(ctx, data)
})
}
}
Loading

0 comments on commit 2cc6390

Please sign in to comment.