func checkExif(f *models.File) (passed bool, err error) {

	var cmdOut []byte
	args := []string{"-j", f.FullPath}
	if cmdOut, err = exec.Command("exiftool", args...).Output(); err != nil {
		return false, errors.Errorf("There was an error retrieving file %s metadata: %s", f.FullPath, err)
	}

	var exifs []models.Exif
	if err = json.Unmarshal(cmdOut, &exifs); err != nil {
		return false, errors.Errorf("There was an error parsing file %s metadata: %s", f.FullPath, err)
	}
	f.Exif = exifs[0]
	f.Save()
	workflowExif, _ := reflections.Items(f.Workflow.Exif)

	var checkError string = ""
	for fieldName, fieldValue := range workflowExif {
		if isZeroOfUnderlyingType(fieldValue) {
			continue
		}
		if value, err := reflections.GetField(f.Exif, fieldName); err != nil {
			return false, errors.Errorf("There was an error validating Exif for file %s. Field '%s' doesn't exist:\n %s", f.FullPath, fieldName, err)
		} else {
			if value != fieldValue {
				checkError += fmt.Sprintf("Field '%s' has value '%v', but '%v' was expected\n", fieldName, value, fieldValue)
			}
		}

	}

	if checkError != "" {
		return false, errors.Errorf("There was an error validating Exif for file %s.\n%s", f.FullPath, checkError)
	} else {
		return true, nil
	}
}
func detectWorkflows(file *models.File, workflows models.Workflows) {
	contentType, _ := file.Attributes["content_type"].(string)
	line, _ := file.Attributes["line"].(string)
	if err := workflows.DetectWorkflows(contentType, line); err != nil {
		file.Status = models.HAS_NO_WORKFLOW
		file.Error = fmt.Sprintf("Could not detect workflows for file: %#v. %s\n", file, err)
		l.Println(file.Error)
	}
	if len(workflows) > 0 {
		if len(workflows) == 1 {
			file.Status = models.HAS_WORKFLOW
			file.Workflow = workflows[0]

		} else {
			file.Status = models.HAS_MANY_WORKFLOWS
			file.Error = fmt.Sprintf("Matched more than one workflow\n: %#v", workflows)
			l.Println(file.Error)
		}
	} else {
		file.Status = models.HAS_NO_WORKFLOW
		file.Error = fmt.Sprintf("No workflows matched by content_type %q and line %q", contentType, line)
		l.Println(file.Error)
	}
}
func TestFileManagerSpec(t *testing.T) {
	Convey("FileManager", t, func() {

		Convey("Importing files", func() {

			Convey("Having one file manager", func() {

				initFileManager()

				Reset(func() {
					fileManager.Destroy()
					fileManager = nil
				})

				Convey("It should prevent watching a->b and a->c simultaneously", func() {
					fileManager.Watch(watchDir1, "a->b")
					err = fileManager.Watch(watchDir1, "a->c")
					So(err, ShouldNotBeNil)
				})

				Convey("It should create source and target directories if not exist", func() {
					fileManager.Watch(watchDir1, "label")

					_, err = os.Stat(watchDir1)
					So(err, ShouldBeNil)

					_, err = os.Stat(targetDir1)
					So(err, ShouldBeNil)
				})

				Convey("It should copy existing file from watch dir to target dir", func() {

					os.MkdirAll(watchDir1, os.ModePerm)

					createTestFile(watchFile1)

					fileManager.Watch(watchDir1, "label")
					So(func() interface{} {
						files, _ := filepath.Glob(targetFile1v1)
						return files
					}, utils.Eventually, 3*time.Second, assertions.ShouldNotBeNil)
				})

				Convey("It should copy only files, not directories", func() {
					subdir := filepath.Join(watchDir1, "subdir")
					os.MkdirAll(subdir, os.ModePerm)
					fileManager.Watch(watchDir1, targetDir1)
					time.Sleep(1 * time.Second)
					_, err = os.Stat(subdir)
					So(err, ShouldBeNil)

				})

				Convey("It should copy new file from watch dir to target dir", func() {

					fileManager.Watch(watchDir1, targetDir1)

					createTestFile(watchFile1)
					So(func() interface{} {
						files, _ := filepath.Glob(targetFile1v1)
						return files
					}, utils.Eventually, 3*time.Second, assertions.ShouldNotBeNil)
				})

				Convey("It should copy 2 new different files to target dir", func() {

					fileManager.Watch(watchDir1, targetDir1)

					watchFile2 := filepath.Join(watchDir1, "file2.txt")
					targetFile2 := filepath.Join(targetDir1, "*/*/v01/file2.txt")

					createTestFile(watchFile1)
					createTestFile(watchFile2)

					So(func() interface{} {
						files, _ := filepath.Glob(targetFile1v1)
						return files
					}, utils.Eventually, 3*time.Second, assertions.ShouldNotBeNil)

					So(func() interface{} {
						files, _ := filepath.Glob(targetFile2)
						return files
					}, utils.Eventually, 3*time.Second, assertions.ShouldNotBeNil)
				})

			})

			Convey("Having two file managers", func() {

				initFileManager()
				if fileManager2, err = fm.NewFM(targetDir2); err != nil {
					panic(fmt.Sprintf("Unable to initialize FileManager2: %v", err))
				}

				Reset(func() {
					fileManager.Destroy()
					fileManager2.Destroy()
					fileManager = nil
					fileManager2 = nil
				})

				Convey("both file managers should move files to target directories", func() {
					fileManager.Watch(watchDir1, targetDir1)
					fileManager2.Watch(watchDir2, targetDir2)
					createTestFile(watchFile1)
					createTestFile(watchFile2)

					So(func() interface{} {
						files, _ := filepath.Glob(targetFile1v1)
						return files
					}, utils.Eventually, 3*time.Second, assertions.ShouldNotBeNil)

					So(func() interface{} {
						files, _ := filepath.Glob(targetFile2v1)
						return files
					}, utils.Eventually, 3*time.Second, assertions.ShouldNotBeNil)
				})

				Convey("after destroying and recreating both file managers files should be moved to target directories", func() {
					fileManager.Destroy()
					fileManager2.Destroy()
					fileManager = nil
					fileManager2 = nil

					if fileManager, err = fm.NewFM(targetDir1, fm.WatchPair{watchDir1, "fm1"}); err != nil {
						panic(fmt.Sprintf("Unable to initialize FileManager: %v", err))
					}
					if fileManager2, err = fm.NewFM(targetDir2, fm.WatchPair{watchDir2, "fm2"}); err != nil {
						panic(fmt.Sprintf("Unable to initialize FileManager2: %v", err))
					}

					createTestFile(watchFile1)
					createTestFile(watchFile2)

					So(func() interface{} {
						files, _ := filepath.Glob(targetFile1v1)
						return files
					}, utils.Eventually, 3*time.Second, assertions.ShouldNotBeNil)

					So(func() interface{} {
						files, _ := filepath.Glob(targetFile2v1)
						return files
					}, utils.Eventually, 3*time.Second, assertions.ShouldNotBeNil)
				})
			})

			Convey("copying more than once the same file to the same dir", func() {
				initFileManager()

				Reset(func() {
					fileManager.Destroy()
					fileManager = nil
				})
				Convey("both files must be versioned", func() {

					fileManager.Watch(watchDir1, "label")

					createTestFile(watchFile1)

					So(func() interface{} {
						files, _ := filepath.Glob(targetFile1v1)
						return files
					}, utils.Eventually, 3*time.Second, assertions.ShouldNotBeNil)

					createTestFile(watchFile1)
					So(func() interface{} {
						files, _ := filepath.Glob(targetFile1v1)
						return files
					}, utils.Eventually, 3*time.Second, assertions.ShouldNotBeNil)
				})

			})
		})

		Convey("Support handlers", func() {
			initFileManager()

			Reset(func() {
				fileManager.Destroy()
				fileManager = nil
			})

			Convey("It registers handlers and calls them", func() {
				handlerWasCalled := false

				handler := func(file *models.File) (err error) {
					handlerWasCalled = true
					return
				}

				fileManager.Register(handler)
				fileManager.Watch(watchDir1, targetDir1)
				createTestFile(watchFile1)

				So(func() interface{} {
					return handlerWasCalled
				}, utils.Eventually, 3*time.Second, assertions.ShouldBeTrue)
			})

			Convey("It calls handler with proper params", func() {
				handlerWasCalled := ""

				handler := func(file *models.File) (err error) {
					handlerWasCalled = file.SourcePath + " " + file.TargetDir
					l.Println(handlerWasCalled)
					return
				}

				fileManager.Register(handler)
				fileManager.Watch(watchDir1, targetDir1)
				createTestFile(watchFile1)

				So(func() interface{} {
					return strings.HasPrefix(handlerWasCalled, watchFile1+" "+targetDir1)
				}, utils.Eventually, 3*time.Second, assertions.ShouldBeTrue)
			})

			Convey("calls more than one handler", func() {
				handlersCalled := 0

				handler1 := func(file *models.File) (err error) {
					handlersCalled++
					return
				}

				handler2 := func(file *models.File) (err error) {
					handlersCalled++
					return
				}

				fileManager.Register(handler1, handler2)

				fileManager.Watch(watchDir1, targetDir1)
				createTestFile(watchFile1)

				So(func() interface{} {
					return handlersCalled == 2
				}, utils.Eventually, 3*time.Second, assertions.ShouldBeTrue)
			})
			Convey("calls handlers until error", func() {
				handlersCalled := false

				handler1 := func(file *models.File) (err error) {
					return errors.New("new error")
				}

				handler2 := func(file *models.File) (err error) {
					handlersCalled = true
					return
				}

				fileManager.Register(handler1, handler2)
				fileManager.Watch(watchDir1, targetDir1)
				createTestFile(watchFile1)

				So(func() interface{} {
					return handlersCalled
				}, utils.Eventually, 3*time.Second, assertions.ShouldBeFalse)
			})
		})

		Convey("Database Integrity", func() {
			initFileManager()

			Reset(func() {
				fileManager.Destroy()
				fileManager = nil
			})

			Convey("It should create versions for the same file and version must increment by one", func() {
				fileManager.Watch(watchDir1, "label")
				createTestFile(watchFile1)

				file := models.File{FileName: filepath.Base(watchFile1)}

				So(func() interface{} {
					return file.Load()
				}, utils.Eventually, 3*time.Second, assertions.ShouldBeNil)

				So(file.Version, ShouldEqual, int64(1))

				files, _ := filepath.Glob(targetFile1v1)
				So(files, ShouldNotBeNil)

				createTestFile(watchFile1)
				file = models.File{FileName: filepath.Base(watchFile1), Version: 2}

				So(func() interface{} {
					return file.Load()
				}, utils.Eventually, 3*time.Second, assertions.ShouldBeNil)

				files, _ = filepath.Glob(targetFile1v2)
				So(files, ShouldNotBeNil)
			})

			Convey("It should create a file record in db", func() {
				fileManager.Watch(watchDir1, "label")

				createTestFile(watchFile1)

				//check that file is in db
				file := models.File{FileName: filepath.Base(watchFile1)}

				So(func() interface{} {
					return file.Load()
				}, utils.Eventually, 3*time.Second, assertions.ShouldBeNil)
			})
		})

		SkipConvey("Validating files", func() {
			Convey("It should validate id3", func() {

			})

			Convey("When file is valid", func() {
				Convey("mark file as valid", func() {
				})
			})

			Convey("When file is invalid", func() {
				Convey("mark file as invalid", func() {
				})
				Convey("send notification to admin", func() {
				})
			})
		})
	})
}