how to handle New Server func in unit testing - testing

for me , unit testing has workload. so i use gotests to generate Boilerplate testing code case.
server.go
func NewServer(cfg *Config, l net.Listener, driver Driver, db store.Store) *Server {
s := &Server{
cfg: cfg,
listener: l,
leader: "",
driver: driver,
db: db,
}
s.server = &http.Server{
Handler: s.createMux(),
}
return s
}
gotests generate server_test.go:
func TestNewServer(t *testing.T) {
fakeCfg := &Config{
Listen: "hello",
LogLevel: "debug",
}
type args struct {
cfg *Config
l net.Listener
leader string
driver Driver
db store.Store
}
tests := []struct {
name string
args args
want *Server
}{
{
name: "test",
args: args{
cfg: fakeCfg,
},
want: &Server{
cfg: fakeCfg,
server: &http.Server{
Handler: nil,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := NewServer(tt.args.cfg, tt.args.l, tt.args.driver, tt.args.db); !reflect.DeepEqual(got, tt.want) {
t.Errorf("NewServer() = %v, want %v", got, tt.want)
}
})
}
}
unit test result:
$ go test -v -run TestNewServer
=== RUN TestNewServer
=== RUN TestNewServer/test
--- FAIL: TestNewServer (0.00s)
--- FAIL: TestNewServer/test (0.00s)
server_test.go:47: NewServer() = &{cfg:0xc4201f5580 listener:<nil> leader: server:0xc4201d6840 driver:<nil> db:<nil> Mutex:{state:0 sema:0}}, want &{cfg:0xc4201f5580 listener:<nil> leader: server:0xc4201d6790 driver:<nil> db:<nil> Mutex:{state:0 sema:0}}
FAIL
exit status 1
FAIL github.com/Dataman-Cloud/swan/api 0.017s
because the initial server struct is not one step. i can't correct get server attribute in &Server{} section.
anyone can do me a favor, give me a hint?howto write test on this situation?

You are comparing the pointers and not the values.
You should change your test to:
!reflect.DeepEqual(*got, *tt.want)
Then you are comparing the content of the structs.

finally i do a trick test, i manually compose every element of the object. it works like a charm.

Related

How to implement new media types in swagger backend

I have created a swagger specification which produces "application/zip"
/config:
get:
produces:
- application/zip
responses:
200: # OK
description: All config files
schema:
type: string
format: binary
I have implemented the handlers for this endpoint but I get this error
http: panic serving 127.0.0.1:20366: applicationZip producer has not yet been implemented
This error is coming from this code
func NewSampleAPI(spec *loads.Document) *SampleAPI {
return &SampleAPI{
...
ApplicationZipProducer: runtime.ProducerFunc(func(w io.Writer, data interface{}) error {
return errors.NotImplemented("applicationZip producer has not yet been implemented")
}),
After investigating this error my findings are that we need to implement something like this
api := operations.NewSampleAPI(swaggerSpec)
api.ApplicationZipProducer = func(w io.Writer, data interface{}) error {
...
}
So my question is that
what should we put in this Producer and why is it necessary to implement this because there is no implementation for "application/json" ?
Is "application/json" Producer is implemented by default and we need to implement other producers?
Note: I am using swagger "2.0" spec
Since you have used "application/zip" as response content then you might have implemented the backend code which might be returning io.ReadCloser.
Then your Producer will look like this
api.ApplicationZipProducer = runtime.ProducerFunc(func(w io.Writer, data interface{}) error {
if w == nil {
return errors.New("ApplicationZipProducer requires a writer") // early exit
}
if data == nil {
return errors.New("no data given to produce zip from")
}
if zp, ok := data.(io.ReadCloser); ok {
b, err := ioutil.ReadAll(zp)
if err != nil {
return fmt.Errorf("application zip producer: %v", err)
}
_, err = w.Write(b)
return nil
}
return fmt.Errorf("%v (%T) is not supported by the ApplicationZipProducer, %s", data, data)
})
This will parse the data interface into io.ReadCloser and read data from it and then it will fill into the io.Writer
Note: If your main purpose is just send file as attachment then you should use "application/octet-stream" and its producer is implemented by default

Set programmatically jsonValidation for dynamic mapping

I am creating a new vscode extension, and I need to extend the standard usage of the jsonValidation system already present in vscode.
Note : I am talking about the system defined in package.json :
"contributes" : {
"languages": [
{
"id" : "yml",
"filenamePatterns": ["module.service"]
},
{
"id" : "json",
"filenamePatterns": ["module.*"]
}
],
"jsonValidation": [
{
"fileMatch": "module.test",
"url": "./resources/test.schema"
}
]
}
Now, I need to create a dynamic mapping, where the json fields filematch/url are defined from some internal rules (like version and other internal stuff). The standard usage is static : one fileMatch -> one schema.
I want for example to read the version from the json file to validate, and set the schema after that :
{
"version" : "1.1"
}
validation schema must be test-schema.1.1 instead of test-schema.1.0
note : The question is only about the modification of the configuration provided by package.json from the extensions.ts
Thanks for the support
** EDIT since the previous solution was not working in all cases
There is one solution to modify the package.json at the activating of the function.
export function activate(context: vscode.ExtensionContext) {
const myPlugin = vscode.extensions.getExtension("your.plugin.id");
if (!myPlugin)
{
throw new Error("Composer plugin is not found...")
}
// Get the current workspace path to found the schema later.
const folderPath = vscode.workspace.workspaceFolders;
if (!folderPath)
{
return;
}
const baseUri : vscode.Uri = folderPath[0].uri;
let packageJSON = myPlugin.packageJSON;
if (packageJSON && packageJSON.contributes && packageJSON.contributes.jsonValidation)
{
let jsonValidation = packageJSON.contributes.jsonValidation;
const schemaUri : vscode.Uri = vscode.Uri.joinPath(baseUri, "/schema/value-0.3.0.json-schema");
const schema = new JsonSchemaMatch("value.ospp", schemaUri)
jsonValidation.push(schema);
}
}
And the json schema class
class JsonSchemaMatch
{
fileMatch: string;
url : string;
constructor(fileMatch : string, url: vscode.Uri)
{
this.fileMatch = fileMatch;
this.url = url.path;
}
}
Another important information is the loading of the element of contributes is not reread after modification, for example
class Language
{
id: string;
filenamePatterns : string[];
constructor(id : string, filenamePatterns: string[])
{
this.id = id;
this.filenamePatterns = filenamePatterns;
}
}
if (packageJSON && packageJSON.contributes && packageJSON.contributes.languages)
{
let languages : Language[] = packageJSON.contributes.languages;
for (let language of languages) {
if (language.id == "json") {
language.filenamePatterns.push("test.my-json-type")
}
}
}
This change has no effect, since the loading of file association is already done (I have not dig for the reason, but I think this is the case)
In this case, creating a settings.json in the workspace directory can do the job:
settings.json
{
"files.associations": {
"target.snmp": "json",
"stack.cfg": "json"
}
}
Be aware that the settings.json can be created by the user with legitimate reason, so don't override it, just fill it.

How to retrieve the underlying error from a Failure Error?

When trying to open a broken epub/ZIP file with epub-rs, the zip-rs crate error (which doesn't use Failure) is wrapped into a failure::Error by epub-rs. I want to handle each error type of zip-rs with an distinct error handler and need a way to match against the underlying error. How can I retrieve it from Failure?
fn main() {
match epub::doc::EpubDoc::new("a.epub") {
Ok(epub) => // do something with the epub
Err(error) => {
// handle errors
}
}
}
error.downcast::<zip::result::ZipError>() fails and error.downcast_ref() returns None.
You can downcast from a Failure Error into another type that implements Fail by using one of three functions:
downcast
downcast_ref
downcast_mut
use failure; // 0.1.5
use std::{fs, io};
fn generate() -> Result<(), failure::Error> {
fs::read_to_string("/this/does/not/exist")?;
Ok(())
}
fn main() {
match generate() {
Ok(_) => panic!("Should have an error"),
Err(e) => match e.downcast_ref::<io::Error>() {
Some(e) => println!("Got an io::Error: {}", e),
None => panic!("Could not downcast"),
},
}
}
For your specific case, I'm guessing that you are either running into mismatched dependency versions (see Why is a trait not implemented for a type that clearly has it implemented? for examples and techniques on how to track this down) or that you simply are getting the wrong error type. For example, a missing file is actually an std::io::Error:
// epub = "1.2.0"
// zip = "0.4.2"
// failure = "0.1.5"
use std::io;
fn main() {
if let Err(error) = epub::doc::EpubDoc::new("a.epub") {
match error.downcast_ref::<io::Error>() {
Some(i) => println!("IO error: {}", i),
None => {
panic!("Other error: {} {:?}", error, error);
}
}
}
}

How to use rendered template in creating a pdf

Ok so I am Go Lang with the Echo framework, to try and build pdf which will load data from a database source - that bit will come later.
So this is how I am rendering my pdf html layout,
func (c *Controller) DataTest(ec echo.Context) error {
return ec.Render(http.StatusOK, "pdf.html", map[string]interface{}{
"name": "TEST",
"msg": "Hello, XXXX!",
})
}
The above function works fine, and renders the html (I built a temp path to the function). Now I want to use that function as my html template to build my pdf's.
So I am using wkhtmltopdf and the lib "github.com/SebastiaanKlippert/go-wkhtmltopdf"
This is how I would render the html in the pdf,
html, err := ioutil.ReadFile("./assets/pdf.html")
if err != nil {
return err
}
But I need to be able to update the template so that is why I am trying to render the page and take that into the pdf.
However, the Echo framework returns an error type and not of type bytes or strings and I am not sure how to update it so my rendered content is returned as bytes?
Thanks,
UPDATE
page := wkhtmltopdf.NewPageReader(bytes.NewReader(c.DataTest(data)))
This is how I am currently doing, the data is just a html string which is then turned into a slice of bytes for the NewReader.
This works fine, but I wanted to turn the DataTest function into a fully rendered html page by Echo. The problem with that is that when you return a rendered page, it is returned as an error type.
So I was trying to work out a why of updating it, so I could return the data as an html string, which would then be put in as a slice of bytes.
if you want rendered html, use echo' custom middleware. I hope it helps you.
main.go
package main
import (
"bufio"
"bytes"
"errors"
"fmt"
"html/template"
"io"
"net"
"net/http"
"github.com/labstack/echo"
)
type TemplateRegistry struct {
templates map[string]*template.Template
}
func (t *TemplateRegistry) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
tmpl, ok := t.templates[name]
if !ok {
err := errors.New("Template not found -> " + name)
return err
}
return tmpl.ExecuteTemplate(w, "base.html", data)
}
func main() {
e := echo.New()
templates := make(map[string]*template.Template)
templates["about.html"] = template.Must(template.ParseFiles("view/about.html", "view/base.html"))
e.Renderer = &TemplateRegistry{
templates: templates,
}
// add custom middleware
// e.Use(PdfMiddleware)
// only AboutHandler for Pdf
e.GET("/about", PdfMiddleware(AboutHandler))
// Start the Echo server
e.Logger.Fatal(e.Start(":8080"))
}
// custom middleware
func PdfMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) (err error) {
resBody := new(bytes.Buffer)
mw := io.MultiWriter(c.Response().Writer, resBody)
writer := &bodyDumpResponseWriter{Writer: mw, ResponseWriter: c.Response().Writer}
c.Response().Writer = writer
if err = next(c); err != nil {
c.Error(err)
}
// or use resBody.Bytes()
fmt.Println(resBody.String())
return
}
}
type bodyDumpResponseWriter struct {
io.Writer
http.ResponseWriter
}
func (w *bodyDumpResponseWriter) WriteHeader(code int) {
w.ResponseWriter.WriteHeader(code)
}
func (w *bodyDumpResponseWriter) Write(b []byte) (int, error) {
return w.Writer.Write(b)
}
func (w *bodyDumpResponseWriter) Flush() {
w.ResponseWriter.(http.Flusher).Flush()
}
func (w *bodyDumpResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
return w.ResponseWriter.(http.Hijacker).Hijack()
}
func AboutHandler(c echo.Context) error {
return c.Render(http.StatusOK, "about.html", map[string]interface{}{
"name": "About",
"msg": "All about Boatswain!",
})
}
view/about.html
{{define "title"}}
Boatswain Blog | {{index . "name"}}
{{end}}
{{define "body"}}
<h1>{{index . "msg"}}</h1>
<h2>This is the about page.</h2>
{{end}}
view/base.html
{{define "base.html"}}
<!DOCTYPE html>
<html>
<head>
<title>{{template "title" .}}</title>
</head>
<body>
{{template "body" .}}
</body>
</html>
{{end}}
So from what I understand you want to:
Render HTML from template
Convert HTML to PDF
Send it as an HTTP response? This part is unclear from your question, but it doesn't matter really.
So, the reason Echo returns error is because it actually not only does the template rendering, but also sending a response to client. If you want to do something else in-between, you can't use that method from echo.
Luckily, echo doesn't do anything magical there. You can just use the standard template package with the same result.
func GetHtml(filename string, data interface{}) (string, error) {
filedata, err := ioutil.ReadFile(filename)
if err != nil {
return "", err
}
asString := string(filedata)
t, err := template.New("any-name").Parse(asString)
if err != nil {
return "", err
}
var buffer bytes.Buffer
err = t.Execute(&buffer, data)
if err != nil {
return "", err
}
return buffer.String(), nil
}
There you have your function that returns a string. You can use buffer.Bytes() to have a byte array if that's preferrable.
After this you can do whatever you need to, like convert to PDF and write it back to clients using echoCtx.Response().Writer().
Hope that helps, but in future try asking more precise questions, then you are more likely to receive an accurate response.

How to include the file path in an IO error in Rust?

In this minimalist program, I'd like the file_size function to include the path /not/there in the Err so it can be displayed in the main function:
use std::fs::metadata;
use std::io;
use std::path::Path;
use std::path::PathBuf;
fn file_size(path: &Path) -> io::Result<u64> {
Ok(metadata(path)?.len())
}
fn main() {
if let Err(err) = file_size(&PathBuf::from("/not/there")) {
eprintln!("{}", err);
}
}
You must define your own error type in order to wrap this additional data.
Personally, I like to use the custom_error crate for that, as it's especially convenient for dealing with several types. In your case it might look like this:
use custom_error::custom_error;
use std::fs::metadata;
use std::io;
use std::path::{Path, PathBuf};
use std::result::Result;
custom_error! {ProgramError
Io {
source: io::Error,
path: PathBuf
} = #{format!("{path}: {source}", source=source, path=path.display())},
}
fn file_size(path: &Path) -> Result<u64, ProgramError> {
metadata(path)
.map(|md| md.len())
.map_err(|e| ProgramError::Io {
source: e,
path: path.to_path_buf(),
})
}
fn main() {
if let Err(err) = file_size(&PathBuf::from("/not/there")) {
eprintln!("{}", err);
}
}
Output:
/not/there: No such file or directory (os error 2)
While Denys Séguret's answer is correct, I like using my crate SNAFU because it provides the concept of a context. This makes the act of attaching the path (or anything else!) very easy to do:
use snafu::{ResultExt, Snafu}; // 0.2.3
use std::{
fs, io,
path::{Path, PathBuf},
};
#[derive(Debug, Snafu)]
enum ProgramError {
#[snafu(display("Could not get metadata for {}: {}", path.display(), source))]
Metadata { source: io::Error, path: PathBuf },
}
fn file_size(path: impl AsRef<Path>) -> Result<u64, ProgramError> {
let path = path.as_ref();
let md = fs::metadata(&path).context(Metadata { path })?;
Ok(md.len())
}
fn main() {
if let Err(err) = file_size("/not/there") {
eprintln!("{}", err);
}
}