Recently I've been working on a Restful app in golang, strange things happened when I try to write tests in different subdirectories. My project structure is:
├── handlers/
│ ├── defs.go
│ ├── hello_test.go
│ ├── hello.go
├── server/
│ ├── codes.go
│ ├── middlewares_test.go
│ ├── middlewares.go
├── utility/
│ ├── auth.go
│ ├── auth_test.go
All files in handlers/ are declared "package handlers", all files in server/ are declared "package server", and so on. When I run go test in utility/ and handlers/ everything is fine. But if I run go test in server/, it returns me nothing but just:
[likejun#localhost server]$ go test
exit status 1
FAIL server_golang/server 0.003s
It seems that it exits with a code 1 before even run, could someone tells me why this happened? Thank you, I've been spent the whole afternoon on it.
the code in middleware_test.go
package server
import (
"io"
"net/http"
"net/http/httptest"
"testing"
)
func TestHello(t *testing.T) {
req, err := http.NewRequest(http.MethodGet, "/", nil)
if err != nil {
t.Fatal(err)
}
rec := httptest.NewRecorder()
func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "application/json")
io.WriteString(w, `{"hello": "world"}`)
}(rec, req)
if status := rec.Code; status != http.StatusOK {
t.Errorf("handler returned wrong status code: got %d want %d", status, http.StatusOK)
}
if rec.Header().Get("Content-Type") != "application/json" {
t.Errorf("handler returned wrong content type header: got %s want %s", rec.Header().Get("Content-Type"), "application/json")
}
expected := `{"hello": "world"}`
if rec.Body.String() != expected {
t.Errorf("handler returned unexpected body: got %s want %s", rec.Body.String(), expected)
}
}
Related
I have this method in my component that is supposed to return a GumBall tokens after the user sends a payment:
pub fn buy_gumball(&self, payment: Bucket) -> Bucket {
self.payments.put(payment.take(self.gumball_cost));
self.gumball_vault.take(1)
}
When I call that method, I get a ResourceCheckFailure:
> resim call-method [component_address] buy_gumball 10,030000000000000000000000000000000000000000000000000004
Instructions:
├─ DeclareTempBucket
├─ CallMethod { component_address: 02c1897261516ff0597fded2b19bf2472ff97b2d791ea50bd02ab2, method: "withdraw", args: [10, 030000000000000000000000000000000000000000000000000004] }
├─ TakeFromContext { amount: 10, resource_address: 030000000000000000000000000000000000000000000000000004, to: Bid(0) }
├─ CallMethod { component_address: 0268709f61e9f60d5d8b8157b5d4939511f194a9f6cfd8656db600, method: "buy_gumball", args: [Bid(0)] }
├─ DropAllBucketRefs
├─ DepositAllBuckets { account: 02c1897261516ff0597fded2b19bf2472ff97b2d791ea50bd02ab2 }
└─ End { signers: [04005feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9] }
Results:
├─ Ok(None)
├─ Ok(Some(Bid(1)))
├─ Ok(None)
└─ Err(ResourceCheckFailure)
Logs: 0
New Entities: 0
Any idea why I get this ?
The Radix Engine makes sure that all buckets are either returned, stored in a vault or burned at the end of a transaction. This is to be confident that no resources will ever get lost because a developer forgot to put the bucket content somewhere.
Imagine you would have sent more than the required payment. The extra XRD would then be lost if the RE didn't do those checks.
In your case, I would suggest to return the payment bucket back to the caller:
pub fn buy_gumball(&self, payment: Bucket) -> (Bucket, Bucket) {
self.payments.put(payment.take(self.gumball_cost));
(self.gumball_vault.take(1), payment)
}
That way, if the user sends more XRD than required, they will get their change back and RE will be happy.
I am using gitlist for the first time and am kind of new to it and im still not the best at git. I have been trying to setup gitlist but I get this error:
Oops!The filename, directory name, or volume label syntax is incorrect.
Is there a way to fix this?
My config.ini file:
[git]
; ; client = '/usr/bin/git' ; Your git executable path
default_branch = 'main' ; Default branch when HEAD is detached
; ; repositories[] = '/repos/' ; Path to your repositories
; ; If you wish to add more repositories, just add a new line
; WINDOWS USERS
client = '"C:\Program Files\Git\bin\git.exe"' ; Your git executable path
repositories[] = 'C:\xampp\htdocs\gitlist\repos' ; Path to your repositories
; You can hide repositories from GitList, just copy this for each repository you want to hide or add a regex (including delimiters), eg. hidden[] = '/(.+)\.git/'
; hidden[] = '/home/git/repositories/BetaTest'
[app]
debug = false
cache = true
theme = "default"
title = "html title"
[clone_button]
; ssh remote
show_ssh_remote = false ; display remote URL for SSH
ssh_host = '' ; host to use for cloning via HTTP (default: none => uses gitlist web host)
ssh_url_subdir = '' ; if cloning via SSH is triggered using special dir (e.g. ssh://example.com/git/repo.git)
; has to end with trailing slash
ssh_port = '' ; port to use for cloning via SSH (default: 22 => standard ssh port)
ssh_user = 'git' ; user to use for cloning via SSH
ssh_user_dynamic = false ; when enabled, ssh_user is set to $_SERVER['PHP_AUTH_USER']
; http remote
show_http_remote = false ; display remote URL for HTTP
http_host = '' ; host to use for cloning via HTTP (default: none => uses gitlist web host)
use_https = true ; generate URL with https://
http_url_subdir = 'git/' ; if cloning via HTTP is triggered using virtual dir (e.g. https://example.com/git/repo.git)
; has to end with trailing slash
http_user = '' ; user to use for cloning via HTTP (default: none)
http_user_dynamic = false ; when enabled, http_user is set to $_SERVER['PHP_AUTH_USER']
; If you need to specify custom filetypes for certain extensions, do this here
[filetypes]
; extension = type
; dist = xml
; If you need to set file types as binary or not, do this here
[binary_filetypes]
; extension = true
; svh = false
; map = true
; set the timezone
[date]
timezone = UTC
format = 'd/m/Y H:i:s'
; custom avatar service
[avatar]
;url = '//gravatar.com/avatar/'
;query[] = 'd=identicon'
This is the folder structure:
├── gitlist/
| ├── the other stuff
│ └── repos/
| └── test/
| └── README.md
The repo is called test and it is in the repos/ folder.
I fixed this by taking away the two double quotes in the client variable:
Before:
client = '"C:\Program Files\Git\bin\git.exe"' ; Your git executable path
After:
client = 'C:\Program Files\Git\bin\git.exe' ; Your git executable path
I'd like to standardize the use of HTTPX for testing regardless of the Python web framework being used. I managed to get it to work with Quart and FastAPI, but I'm having issues with Tornado since it doesn't comply to ASGI, and it uses a particular asynchronous implementation, although it is currently based on asyncio.
The minimal application to test is divided in three parts: main.py, conftest.py and test_hello.py.
app/main.py:
from contextlib import contextmanager
from typing import Iterator
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
from tornado.web import Application, RequestHandler
from loguru import logger
async def start_resources() -> None:
'''
Initialize resources such as async Redis and Database connections
'''
logger.info('resources started...')
async def close_resources() -> None:
'''
Release resources
'''
logger.info('resources closed...')
class HelloHandler(RequestHandler):
def get(self) -> None:
self.write({'hello': 'world'})
#contextmanager
def create_app() -> Iterator[Application]:
IOLoop.current().run_sync(start_resources)
try:
app = Application([
("/hello", HelloHandler),
])
yield app
finally:
IOLoop.current().run_sync(close_resources)
if __name__ == '__main__':
with create_app() as app:
http_server = HTTPServer(app)
http_server.listen(8000)
logger.info('Listening to port 8000 (use CTRL + C to quit)')
IOLoop.current().start()
tests/conftest.py:
from typing import Iterator, AsyncIterable
from httpx import AsyncClient
from pytest import fixture
from tornado.platform.asyncio import AsyncIOLoop
from tornado.web import Application
from app.main import create_app # isort:skip
#fixture
def app(io_loop: AsyncIOLoop) -> Iterator[Application]:
'''
Return a Tornado.web.Application object with initialized resources
'''
with create_app() as app:
yield app
#fixture
async def client(app: Application,
base_url: str) -> AsyncIterable[AsyncClient]:
async with AsyncClient(base_url=base_url) as _client:
yield _client
tests/test_hello.py:
from httpx import AsyncClient
from pytest import mark
#mark.gen_test
async def test_hello(client: AsyncClient) -> None:
resp = await client.get('/hello')
assert resp.status_code == 200
assert resp.json() == {'hello': 'world'}
And the project structure is this:
.
├── app
│ ├── __init__.py
│ └── main.py
├── poetry.lock
├── pyproject.toml
└── tests
├── conftest.py
├── __init__.py
└── test_hello.py
And the error I get
$ pytest tests/test_hello.py
========================================================================== test session starts ==========================================================================
platform linux -- Python 3.6.9, pytest-5.4.3, py-1.8.2, pluggy-0.13.1
rootdir: /tmp/minimal-app
plugins: tornado-0.8.1
collected 1 item
tests/test_hello.py F [100%]
=============================================================================== FAILURES ================================================================================
______________________________________________________________________________ test_hello _______________________________________________________________________________
client = <async_generator object client at 0x7f78e3de75f8>
#mark.gen_test
async def test_hello(client: AsyncClient) -> None:
> resp = await client.get('/hello')
E AttributeError: 'async_generator' object has no attribute 'get'
tests/test_hello.py:7: AttributeError
------------------------------------------------------------------------- Captured stderr setup -------------------------------------------------------------------------
2020-06-17 10:21:28.574 | INFO | app.main:start_resources:15 - resources started...
----------------------------------------------------------------------- Captured stderr teardown ------------------------------------------------------------------------
2020-06-17 10:21:28.595 | INFO | app.main:close_resources:22 - resources closed...
======================================================================== short test summary info ========================================================================
FAILED tests/test_hello.py::test_hello - AttributeError: 'async_generator' object has no attribute 'get'
=========================================================================== 1 failed in 0.03s ===========================================================================
I could make it work replacing pytest-tornado fixtures for a custom one and adding alt-pytest-asyncio to support asynchronous tests. pytest-tornado is not necessary anymore.
conftest.py:
from typing import AsyncIterable, Iterator
from httpx import AsyncClient
from pytest import fixture
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
from tornado.platform.asyncio import AsyncIOLoop
from tornado.testing import bind_unused_port
from tornado.web import Application
from app.main import create_app # isort:skip
#fixture
def io_loop() -> AsyncIOLoop:
'''
Copied from https://github.com/eukaryote/pytest-tornasync/blob/master/src/pytest_tornasync/plugin.py#L59-L68
'''
loop = IOLoop()
loop.make_current()
yield loop
loop.clear_current()
loop.close(all_fds=True)
#fixture
def app(io_loop: AsyncIOLoop) -> Iterator[Application]:
'''
Return a Tornado.web.Application object with initialized resources
'''
with create_app() as app:
yield app
#fixture
async def client(app: Application) -> AsyncIterable[AsyncClient]:
'''
Start a HTTPServer each time
'''
http_server = HTTPServer(app)
port = bind_unused_port()[1]
http_server.listen(port)
async with AsyncClient(base_url=f'http://localhost:{port}') as _client:
yield _client
pyproject.toml:
[tool.poetry.dependencies]
python = "^3.8"
tornado = "^6.0.4"
pytest = "^6.0.1"
httpx = "^0.13.3"
loguru = "^0.5.1"
alt-pytest-asyncio = "^0.5.3"
[tool.poetry.dev-dependencies]
[build-system]
requires = ["poetry>=0.12"]
build-backend = "poetry.masonry.api"
elm 0.19
$ mkdir myprj; cd myprj; elm init; elm install elm/http
then create src/test.elm and src/test.txt:
$ tree
.
├── elm.json
└── src
├── test.elm
└── test.txt
$ elm reactor
then navigate to:
http://localhost:8000/src/test.elm
so the browser window shows:
This is a headless program, meaning there is nothing to show here.
I started the program anyway though, and you can access it as `app` in the developer console.
but the browser console shows:
Failed to load resource: the server responded with a status of 404 (Not Found) test.text:1
Why can't elm reactor locate test.txt?
test.txt:
hi
test.elm:
import Http
init () =
( "", Http.send Resp <| Http.getString "test.text" )
type Msg
= Resp (Result Http.Error String)
update msg model =
case msg of
Resp result ->
case result of
Ok body ->
( Debug.log "ok" body, Cmd.none )
Err _ ->
( "err", Cmd.none )
subscriptions model =
Sub.none
main =
Platform.worker
{ init = init
, update = update
, subscriptions = subscriptions
}
Solved
In test.elm, the url "test.txt" was falsely spelled to "test.text".
Your comment has different file extensions. You said you created src/test.txt but you are getting a 404 because you are asking for a .text extension.
Try going to http://localhost:8000/src/test.txt
Is it possible to use the Grafana Http API with client-side javascript?
I start with the very basic of getting the json of an already created dashboard.
function getHome2Dashboard(callback) {
$.ajax({
type: 'GET',
url: 'http://localhost:3000/api/dashboards/db/home-2',
crossDomain: true,
dataType: 'json',
headers: {
"Authorization": "Bearer eyJrIjoiYkdURk91VkNQSTF3OVdBWmczYUNoYThPOGs0QTJWZVkiLCJuIjoidGVzdDEiLCJpZCI6MX0="
},
success: function(data)
{
if( callback ) callback(data);
},
error: function(err)
{
console.log(err);
}
});
but I get a:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
I also tried using a jsonp approach, dev tools show that the server sends back the json data but js fails because (I think) the result is not wrapped inside a callback function. Any suggestions on how to implement this are welcome...
// At the moment I think of something like:
┌──────────┐ ┌───────────┐
│ Browser │ <-------> │ Grafana │
└──────────┘ └───────────┘
// In order to overcome the cross-origin problems,
// should I go towards something like this?:
┌──────────┐ ┌───────────┐ ┌───────────┐
│ Browser │ <-------> │ Web api │ <-------> │ Grafana │
└──────────┘ └───────────┘ └───────────┘
According to these links there is currently no way in Grafana to set CORS headers directly in the server, so you will need to put the Grafana server behind a reverse proxy like nginx and add the headers there.
See the Mozilla Developer documentation about CORS to understand the issue you are facing.